From 3904f40323e3d67f89dff6f76bd7bdaf3c82447c Mon Sep 17 00:00:00 2001 From: Max Vasin Limsukhawat Date: Tue, 21 May 2024 22:44:28 -0600 Subject: [PATCH 1/2] refactor: Reduce ERC1155RoyalitySoulboundV1 size --- constants/constructor-args.ts | 6 +- constants/contract.ts | 4 +- contracts/libraries/LibItems.sol | 7 +- .../ERC1155RoyaltiesSoulboundV1.sol | 92 ++-- .../ERC1155RoyaltiesSoulboundV2.sol | 417 ------------------ 5 files changed, 34 insertions(+), 492 deletions(-) delete mode 100644 contracts/upgradeables/soulbounds/ERC1155RoyaltiesSoulboundV2.sol diff --git a/constants/constructor-args.ts b/constants/constructor-args.ts index 09747846..845339b5 100644 --- a/constants/constructor-args.ts +++ b/constants/constructor-args.ts @@ -144,10 +144,10 @@ export const AvatarBoundArgs = { baseURI: '', contractURI: 'https://achievo.mypinata.cloud/ipfs/QmSDbeNWVY2CGUuLHni689L5eSrSG3iZHyTRZJWXX7GpjS', revealURI: 'Qmdk4zHamwCyqSzuWNNYypuz4FXAGdApbky7SHNsXYYQg7', + compoundURI: 'https://api.achievo.xyz/v1/uri/avatar', devWallet: 'DEPLOYER_WALLET', gatingNftAddress: '0xD07180c423F9B8CF84012aA28cC174F3c433EE29', itemsNftAddress: `CONTRACT_${CONTRACT_NAME.Items}`, - compoundURI: 'https://api.achievo.xyz/v1/uri/avatar', mintNftGatingEnabled: true, mintNFtWithoutGatingEnabled: true, mintRandomItemEnabled: true, @@ -159,10 +159,10 @@ export const AvatarBoundArgs = { baseURI: '', contractURI: 'https://achievo.mypinata.cloud/ipfs/QmSDbeNWVY2CGUuLHni689L5eSrSG3iZHyTRZJWXX7GpjS', revealURI: 'QmZnvSyeKRQxWwcofVmq41BNCtHbBmomk8Ny8mtGRTjtzS', + compoundURI: 'https://staging-api.achievo.xyz/v1/uri/avatar', devWallet: 'DEPLOYER_WALLET', gatingNftAddress: '0x6E03Ea6c9aBBb78Dd761b9c71c06176c508488C3', itemsNftAddress: `CONTRACT_${CONTRACT_NAME.Items}`, - compoundURI: 'https://staging-api.achievo.xyz/v1/uri/avatar', mintNftGatingEnabled: true, mintNFtWithoutGatingEnabled: true, mintRandomItemEnabled: true, @@ -514,4 +514,4 @@ export const EnglishAuctionsExtensionArgs = { export const OffersExtensionArgs = { MAINNET: {}, TESTNET: {}, -}; \ No newline at end of file +}; diff --git a/constants/contract.ts b/constants/contract.ts index 06da5813..97d58c2d 100644 --- a/constants/contract.ts +++ b/constants/contract.ts @@ -68,7 +68,7 @@ export enum CONTRACT_FILE_NAME { export enum CONTRACT_UPGRADABLE_FILE_NAME { LegacyAvatar = 'LegacyAvatarUpgradeableV1', Avatars = 'AvatarBoundV1', - ERC1155RoyaltiesSoulbound = 'ERC1155RoyaltiesSoulboundV2', + ERC1155RoyaltiesSoulbound = 'ERC1155RoyaltiesSoulboundV1', Levels = 'LevelsBoundV1', ERC20ChainlinkPaymaster = 'ERC20ChainlinkPaymasterV2', ERC1155Soulbound = 'ERC1155SoulboundV1', @@ -134,5 +134,5 @@ export enum CONTRACT_UPGRADABLE_NAME { export enum CONTRACT_EXTENSION_NAME { DirectListingExtension = 'DirectListingExtension', - EnglishAuctionsExtension = 'EnglishAuctionsExtension' + EnglishAuctionsExtension = 'EnglishAuctionsExtension', } diff --git a/contracts/libraries/LibItems.sol b/contracts/libraries/LibItems.sol index b2d495a2..0b7f258c 100644 --- a/contracts/libraries/LibItems.sol +++ b/contracts/libraries/LibItems.sol @@ -22,11 +22,6 @@ library LibItems { string tierName; } - struct TokenCreate { - uint256 tokenId; - string tokenUri; - } - enum RewardType { ETHER, ERC20, @@ -57,7 +52,7 @@ library LibItems { uint256 maxSupply; // 0 mean unlimited } - struct TokenCreateWithRoyalty { + struct TokenCreate { uint256 tokenId; string tokenUri; address receiver; diff --git a/contracts/upgradeables/soulbounds/ERC1155RoyaltiesSoulboundV1.sol b/contracts/upgradeables/soulbounds/ERC1155RoyaltiesSoulboundV1.sol index f70f5587..454286c0 100644 --- a/contracts/upgradeables/soulbounds/ERC1155RoyaltiesSoulboundV1.sol +++ b/contracts/upgradeables/soulbounds/ERC1155RoyaltiesSoulboundV1.sol @@ -44,6 +44,13 @@ import { ERCWhitelistSignatureUpgradeable } from "../ercs/ERCWhitelistSignatureU import { LibItems } from "../../libraries/LibItems.sol"; error InvalidSeed(); +error InvalidInput(); +error AddressIsZero(); +error ExceedMaxMint(); +error MissingRole(); +error TokenNotExist(); +error TokenMintPaused(); +error DuplicateID(); contract ERC1155RoyaltiesSoulboundV1 is Initializable, @@ -80,7 +87,7 @@ contract ERC1155RoyaltiesSoulboundV1 is modifier maxPerMintCheck(uint256 amount) { if (amount > MAX_PER_MINT) { - revert("ExceedMaxMint"); + revert ExceedMaxMint(); } _; } @@ -109,7 +116,9 @@ contract ERC1155RoyaltiesSoulboundV1 is __Achievo1155SoulboundUpgradable_init(); __ERCWhitelistSignatureUpgradeable_init(); - require(devWallet != address(0), "AddressIsZero"); + if (devWallet == address(0)) { + revert AddressIsZero(); + } _grantRole(DEFAULT_ADMIN_ROLE, devWallet); _grantRole(MINTER_ROLE, devWallet); @@ -126,16 +135,20 @@ contract ERC1155RoyaltiesSoulboundV1 is if (_isPaused) _pause(); } - function getAllItems() public view returns (LibItems.TokenReturn[] memory) { + function getAllItems(address _owner) public view returns (LibItems.TokenReturn[] memory) { + bool isAdmin = hasRole(MINTER_ROLE, _msgSender()); + if (!isAdmin && _owner != _msgSender()) { + revert MissingRole(); + } uint256 totalTokens = itemIds.length; LibItems.TokenReturn[] memory tokenReturns = new LibItems.TokenReturn[](totalTokens); uint index; for (uint i = 0; i < totalTokens; i++) { uint256 tokenId = itemIds[i]; - uint256 amount = balanceOf(_msgSender(), tokenId); + uint256 amount = balanceOf(_owner, tokenId); - if (amount > 0) { + if (isAdmin || amount > 0) { LibItems.TokenReturn memory tokenReturn = LibItems.TokenReturn({ tokenId: tokenId, tokenUri: uri(tokenId), @@ -155,38 +168,9 @@ contract ERC1155RoyaltiesSoulboundV1 is return returnsTruncated; } - function getAllItemsAdmin( - address _owner - ) public view onlyRole(MINTER_ROLE) returns (LibItems.TokenReturn[] memory) { - uint256 totalTokens = itemIds.length; - LibItems.TokenReturn[] memory tokenReturns = new LibItems.TokenReturn[](totalTokens); - - uint index; - for (uint i = 0; i < totalTokens; i++) { - uint256 tokenId = itemIds[i]; - uint256 amount = balanceOf(_owner, tokenId); - - LibItems.TokenReturn memory tokenReturn = LibItems.TokenReturn({ - tokenId: tokenId, - tokenUri: uri(tokenId), - amount: amount - }); - tokenReturns[index] = tokenReturn; - index++; - } - - // truncate the array - LibItems.TokenReturn[] memory returnsTruncated = new LibItems.TokenReturn[](index); - for (uint i = 0; i < index; i++) { - returnsTruncated[i] = tokenReturns[i]; - } - - return returnsTruncated; - } - function isTokenExist(uint256 _tokenId) public view returns (bool) { if (!tokenExists[_tokenId]) { - revert("TokenNotExist"); + revert TokenNotExist(); } return true; } @@ -236,6 +220,10 @@ contract ERC1155RoyaltiesSoulboundV1 is tokenUris[_token.tokenId] = _token.tokenUri; } + if (_token.receiver != address(0)) { + _setTokenRoyalty(_token.tokenId, _token.receiver, uint96(_token.feeBasisPoints)); + } + tokenExists[_token.tokenId] = true; itemIds.push(_token.tokenId); @@ -248,30 +236,6 @@ contract ERC1155RoyaltiesSoulboundV1 is } } - function addNewTokenWithRoyalty(LibItems.TokenCreateWithRoyalty calldata _token) public onlyRole(DEV_CONFIG_ROLE) { - if (_token.receiver == address(0)) { - revert("ReceiverAddressZero"); - } - - if (bytes(_token.tokenUri).length > 0) { - tokenUris[_token.tokenId] = _token.tokenUri; - } - - tokenExists[_token.tokenId] = true; - - itemIds.push(_token.tokenId); - - _setTokenRoyalty(_token.tokenId, _token.receiver, uint96(_token.feeBasisPoints)); - } - - function addNewTokensWithRoyalty( - LibItems.TokenCreateWithRoyalty[] calldata _tokens - ) external onlyRole(DEV_CONFIG_ROLE) { - for (uint256 i = 0; i < _tokens.length; i++) { - addNewTokenWithRoyalty(_tokens[i]); - } - } - function updateTokenUri(uint256 _tokenId, string calldata _tokenUri) public onlyRole(DEV_CONFIG_ROLE) { tokenUris[_tokenId] = _tokenUri; } @@ -281,7 +245,7 @@ contract ERC1155RoyaltiesSoulboundV1 is string[] calldata _tokenUris ) public onlyRole(DEV_CONFIG_ROLE) { if (_tokenIds.length != _tokenUris.length) { - revert("InvalidInput"); + revert InvalidInput(); } for (uint256 i = 0; i < _tokenIds.length; i++) { updateTokenUri(_tokenIds[i], _tokenUris[i]); @@ -297,7 +261,7 @@ contract ERC1155RoyaltiesSoulboundV1 is uint256 _id = _tokenIds[i]; isTokenExist(_id); if (isTokenMintPaused[_id]) { - revert("TokenMintPaused"); + revert TokenMintPaused(); } if (soulbound) { @@ -334,7 +298,7 @@ contract ERC1155RoyaltiesSoulboundV1 is isTokenExist(id); if (isTokenMintPaused[id]) { - revert("TokenMintPaused"); + revert TokenMintPaused(); } if (soulbound) { @@ -382,7 +346,7 @@ contract ERC1155RoyaltiesSoulboundV1 is uint256 id = _ids[i]; if (tokenIdProcessed[_from][id]) { - revert("ERC1155: duplicate ID"); + revert DuplicateID(); } tokenIdProcessed[_from][id] = true; @@ -439,7 +403,7 @@ contract ERC1155RoyaltiesSoulboundV1 is uint256 id = tokenIds[i]; if (tokenIdProcessed[to][id]) { - revert("ERC1155: duplicate ID"); + revert DuplicateID(); } tokenIdProcessed[to][id] = true; diff --git a/contracts/upgradeables/soulbounds/ERC1155RoyaltiesSoulboundV2.sol b/contracts/upgradeables/soulbounds/ERC1155RoyaltiesSoulboundV2.sol deleted file mode 100644 index 1c613298..00000000 --- a/contracts/upgradeables/soulbounds/ERC1155RoyaltiesSoulboundV2.sol +++ /dev/null @@ -1,417 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.17; - -/** - * Author: Achievo Team - (https://achievo.xyz/) - */ - -//TODO: This contract is deprecated USE THE ERC1155SoulboundV1.sol - -// MMMMNkc. .,oKWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM -// MWXd,. .cONMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM -// Wx' .cKMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM -// x. ;KMMMMMMMMMMMMWKxlcclxKWMMMMMMMMWKxlc::::::::ckWMMXd:cOMMMMMMMMKo:oKMMWkccxWMWKdccccccccccccoKMM0l:l0MMMMMMMMMWkc:dXMMMXkoc::::::clxKW -// ' lNMMMMMMMMMMNd. .. .dNMMMMMMNd. ..........oWMM0' oWMMMMMMMk. .kMMN: :XMNl .''''''''';OMMX: ,0MMMMMMMWk. oNMWk' ........ .o -// . :XMMMMMMMMMWd. .o00l. .dWMMMMWx. .o0KKXKKXXXXNMMM0' oNWWWWWWWk. .kMMN: :NMNc .kNNNNNNNNNNWMMM0, :XMMMMMM0, cXMMO. c0KKKKXK0o. -// , .lkxo. ;dkx, oWMMMMMMMMWk. oNMMNo. .kWMMMWl ;KMMMMMMMMMMMMMM0' .',',,,,,. .kMMN: :NMNc ,:;;;;;;dXMMMMMMO. lNMMMMK: ;KMMMd. .OMMMMMMMMX; -// : :KWX: .xMWx. .kMMMMMMMMM0' cXMMMMXc ,0MMMWl ;KMMMMMMMMMMMMMM0' .',,'',,,. .kMMN: :NMNc ',,;;,;;oXMMMMMMWx. .dWMMNc 'OMMMMd. .OMMMMMMMMX; -// l ,0WO:oXWd. .OMMMMMMMMK; ;KMMMMMMK; :KMMWd. .o0KKXXKKKXXNMMM0' oNWWWWWWWx. .kMMN: :XMNc .kNNNNNNNNWWWMMMMMNo. .dK0l. .xWMMMMO. .c0KKKXXK0o. -// o dWMWWMK, '0MMMMMMMXc 'OMMMMMMMMO' cNMMNd. ..........oWMM0' oWMMMMMMMk. .kMMN: :XMNl .,,,,,,,,,:0MMMMMMNo. .. 'xWMMMMMWx' ....... .o -// O' :XMMMMk. cXMMMMMMMKo:cOWMMMMMMMMWOc:oKMMMWKxlc::::::::ckWMMXd:cOMMMMMMMMKo:oKMMWkc:xWMWKoc:::::::::::lKMMMMMMMWKdlcclxXWMMMMMMMMXkoc::::::clxKW -// WO; 'OMMMWl .oXMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM -// MMNx'.dWMMK;.:0WMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM -// MMMM0cdNMM0cdNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM - -import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; -import { ERC1155Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC1155/ERC1155Upgradeable.sol"; -import { - ERC1155BurnableUpgradeable -} from "@openzeppelin/contracts-upgradeable/token/ERC1155/extensions/ERC1155BurnableUpgradeable.sol"; -import { - ERC1155SupplyUpgradeable -} from "@openzeppelin/contracts-upgradeable/token/ERC1155/extensions/ERC1155SupplyUpgradeable.sol"; -import { AccessControlUpgradeable } from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; -import { PausableUpgradeable } from "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol"; -import { StringsUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; -import { ERC2981Upgradeable } from "@openzeppelin/contracts-upgradeable/token/common/ERC2981Upgradeable.sol"; -import { ECDSAUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol"; -import { - ReentrancyGuardUpgradeable -} from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; - -import { Achievo1155SoulboundUpgradeable } from "../ercs/extensions/Achievo1155SoulboundUpgradeable.sol"; -import { ERCWhitelistSignatureUpgradeable } from "../ercs/ERCWhitelistSignatureUpgradeable.sol"; -import { LibItems } from "../../libraries/LibItems.sol"; - -contract ERC1155RoyaltiesSoulboundV2 is - Initializable, - ERC1155BurnableUpgradeable, - ERC1155SupplyUpgradeable, - Achievo1155SoulboundUpgradeable, - ERC2981Upgradeable, - ERCWhitelistSignatureUpgradeable, - AccessControlUpgradeable, - PausableUpgradeable, - ReentrancyGuardUpgradeable -{ - event ContractURIChanged(string indexed uri); - - bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); - bytes32 public constant MANAGER_ROLE = keccak256("MANAGER_ROLE"); - bytes32 public constant DEV_CONFIG_ROLE = keccak256("DEV_CONFIG_ROLE"); - - string public contractURI; - string private baseURI; - string public name; - string public symbol; - using StringsUpgradeable for uint256; - - uint256 public MAX_PER_MINT; - - mapping(uint256 => bool) private tokenExists; - mapping(uint256 => string) public tokenUris; // tokenId => tokenUri - mapping(uint256 => bool) public isTokenMintPaused; // tokenId => bool - default is false - - uint256[] public itemIds; - - mapping(address => mapping(uint256 => bool)) private tokenIdProcessed; - - event MintedId(address indexed to, uint256 indexed tokenId, uint256 amount, bool soulbound); - event TokenAdded(uint256 indexed tokenId); - - /// @custom:oz-upgrades-unsafe-allow constructor - constructor() { - _disableInitializers(); - } - - function initialize( - string memory _name, - string memory _symbol, - string memory _initBaseURI, - string memory _contractURI, - uint256 _maxPerMint, - bool _isPaused, - address devWallet - ) public initializer { - __ERC1155_init(""); - __ReentrancyGuard_init(); - __AccessControl_init(); - __Achievo1155SoulboundUpgradable_init(); - __ERCWhitelistSignatureUpgradeable_init(); - - require(devWallet != address(0), "AddressIsZero"); - - _grantRole(DEFAULT_ADMIN_ROLE, devWallet); - _grantRole(MINTER_ROLE, devWallet); - _grantRole(MANAGER_ROLE, devWallet); - _grantRole(DEV_CONFIG_ROLE, devWallet); - _addWhitelistSigner(devWallet); - - name = _name; - symbol = _symbol; - baseURI = _initBaseURI; - contractURI = _contractURI; - MAX_PER_MINT = _maxPerMint; - - if (_isPaused) _pause(); - } - - function getAllItems() public view returns (LibItems.TokenReturn[] memory) { - uint256 totalTokens = itemIds.length; - LibItems.TokenReturn[] memory tokenReturns = new LibItems.TokenReturn[](totalTokens); - - uint index; - for (uint i = 0; i < totalTokens; i++) { - uint256 tokenId = itemIds[i]; - uint256 amount = balanceOf(_msgSender(), tokenId); - - if (amount > 0) { - LibItems.TokenReturn memory tokenReturn = LibItems.TokenReturn({ - tokenId: tokenId, - tokenUri: uri(tokenId), - amount: amount - }); - tokenReturns[index] = tokenReturn; - index++; - } - } - - // truncate the array - LibItems.TokenReturn[] memory returnsTruncated = new LibItems.TokenReturn[](index); - for (uint i = 0; i < index; i++) { - returnsTruncated[i] = tokenReturns[i]; - } - - return returnsTruncated; - } - - function isTokenExist(uint256 _tokenId) public view returns (bool) { - if (!tokenExists[_tokenId]) { - revert("TokenNotExist"); - } - return true; - } - - function decodeData(bytes calldata _data) public view onlyRole(DEV_CONFIG_ROLE) returns (uint256[] memory) { - return _decodeData(_data); - } - - function _decodeData(bytes calldata _data) private view returns (uint256[] memory) { - uint256[] memory itemIds = abi.decode(_data, (uint256[])); - return itemIds; - } - - function pause() external onlyRole(MANAGER_ROLE) { - _pause(); - } - - function unpause() external onlyRole(MANAGER_ROLE) { - _unpause(); - } - - function addNewToken(LibItems.TokenCreate calldata _token) public onlyRole(DEV_CONFIG_ROLE) { - if (bytes(_token.tokenUri).length > 0) { - tokenUris[_token.tokenId] = _token.tokenUri; - } - - tokenExists[_token.tokenId] = true; - - itemIds.push(_token.tokenId); - emit TokenAdded(_token.tokenId); - } - - function addNewTokens(LibItems.TokenCreate[] calldata _tokens) external onlyRole(DEV_CONFIG_ROLE) { - for (uint256 i = 0; i < _tokens.length; i++) { - addNewToken(_tokens[i]); - } - } - - function addNewTokenWithRoyalty(LibItems.TokenCreateWithRoyalty calldata _token) public onlyRole(DEV_CONFIG_ROLE) { - if (_token.receiver == address(0)) { - revert("ReceiverAddressZero"); - } - - if (bytes(_token.tokenUri).length > 0) { - tokenUris[_token.tokenId] = _token.tokenUri; - } - - tokenExists[_token.tokenId] = true; - - itemIds.push(_token.tokenId); - - _setTokenRoyalty(_token.tokenId, _token.receiver, uint96(_token.feeBasisPoints)); - } - - function addNewTokensWithRoyalty( - LibItems.TokenCreateWithRoyalty[] calldata _tokens - ) external onlyRole(DEV_CONFIG_ROLE) { - for (uint256 i = 0; i < _tokens.length; i++) { - addNewTokenWithRoyalty(_tokens[i]); - } - } - - function updateTokenUri(uint256 _tokenId, string calldata _tokenUri) public onlyRole(DEV_CONFIG_ROLE) { - tokenUris[_tokenId] = _tokenUri; - } - - function batchUpdateTokenUri( - uint256[] calldata _tokenIds, - string[] calldata _tokenUris - ) public onlyRole(DEV_CONFIG_ROLE) { - if (_tokenIds.length != _tokenUris.length) { - revert("InvalidInput"); - } - for (uint256 i = 0; i < _tokenIds.length; i++) { - updateTokenUri(_tokenIds[i], _tokenUris[i]); - } - } - - function updateTokenMintPaused(uint256 _tokenId, bool _isTokenMintPaused) public onlyRole(MANAGER_ROLE) { - isTokenMintPaused[_tokenId] = _isTokenMintPaused; - } - - function adminMintId( - address to, - uint256 id, - uint256 amount, - bool soulbound - ) external onlyRole(MINTER_ROLE) whenNotPaused { - isTokenExist(id); - - if (isTokenMintPaused[id]) { - revert("TokenMintPaused"); - } - - if (soulbound) { - _soulbound(to, id, amount); - } - - _mint(to, id, amount, ""); - emit MintedId(to, id, amount, soulbound); - } - - function _beforeTokenTransfer( - address operator, - address from, - address to, - uint256[] memory ids, - uint256[] memory amounts, - bytes memory data - ) internal virtual override(ERC1155Upgradeable, ERC1155SupplyUpgradeable) { - super._beforeTokenTransfer(operator, from, to, ids, amounts, data); - } - - function safeTransferFrom( - address _from, - address _to, - uint256 _id, - uint256 _amount, - bytes memory _data - ) public virtual override soulboundCheckAndSync(_from, _to, _id, _amount, balanceOf(_from, _id)) { - super.safeTransferFrom(_from, _to, _id, _amount, _data); - } - - function safeBatchTransferFrom( - address _from, - address _to, - uint256[] memory _ids, - uint256[] memory _amounts, - bytes memory _data - ) - public - virtual - override - soulboundCheckAndSyncBatch(_from, _to, _ids, _amounts, balanceOfBatchOneAccount(_from, _ids)) - { - for (uint256 i = 0; i < _ids.length; i++) { - uint256 id = _ids[i]; - - if (tokenIdProcessed[_from][id]) { - revert("ERC1155: duplicate ID"); - } - - tokenIdProcessed[_from][id] = true; - } - - super.safeBatchTransferFrom(_from, _to, _ids, _amounts, _data); - - // Reset processed status after the transfer is completed - for (uint256 i = 0; i < _ids.length; i++) { - uint256 id = _ids[i]; - tokenIdProcessed[_from][id] = false; - } - } - - function balanceOfBatchOneAccount( - address account, - uint256[] memory ids - ) public view virtual returns (uint256[] memory) { - uint256[] memory batchBalances = new uint256[](ids.length); - - for (uint256 i = 0; i < ids.length; ++i) { - batchBalances[i] = balanceOf(account, ids[i]); - } - - return batchBalances; - } - - function burn( - address to, - uint256 tokenId, - uint256 amount - ) - public - virtual - override - nonReentrant - soulboundCheckAndSync(to, address(0), tokenId, amount, balanceOf(to, tokenId)) - { - ERC1155BurnableUpgradeable.burn(to, tokenId, amount); - } - - function burnBatch( - address to, - uint256[] memory tokenIds, - uint256[] memory amounts - ) - public - virtual - override - nonReentrant - soulboundCheckAndSyncBatch(to, address(0), tokenIds, amounts, balanceOfBatchOneAccount(to, tokenIds)) - { - for (uint256 i = 0; i < tokenIds.length; i++) { - uint256 id = tokenIds[i]; - - if (tokenIdProcessed[to][id]) { - revert("ERC1155: duplicate ID"); - } - - tokenIdProcessed[to][id] = true; - } - - ERC1155BurnableUpgradeable.burnBatch(to, tokenIds, amounts); - - // Reset processed status after the transfer is completed - for (uint256 i = 0; i < tokenIds.length; i++) { - uint256 id = tokenIds[i]; - tokenIdProcessed[to][id] = false; - } - } - - function supportsInterface( - bytes4 interfaceId - ) public view override(ERC1155Upgradeable, ERC2981Upgradeable, AccessControlUpgradeable) returns (bool) { - return super.supportsInterface(interfaceId); - } - - function uri(uint256 tokenId) public view override returns (string memory) { - isTokenExist(tokenId); - if (bytes(tokenUris[tokenId]).length > 0) { - return tokenUris[tokenId]; - } else { - return string(abi.encodePacked(baseURI, "/", tokenId.toString())); - } - } - - function setRoyaltyInfo(address receiver, uint96 feeBasisPoints) external onlyRole(MANAGER_ROLE) { - _setDefaultRoyalty(receiver, feeBasisPoints); - } - - function setTokenRoyalty(uint256 tokenId, address receiver, uint96 feeBasisPoints) external onlyRole(MANAGER_ROLE) { - _setTokenRoyalty(tokenId, receiver, uint96(feeBasisPoints)); - } - - function resetTokenRoyalty(uint256 tokenId) external onlyRole(MANAGER_ROLE) { - _resetTokenRoyalty(tokenId); - } - - function updateWhitelistAddress(address _address, bool _isWhitelisted) external onlyRole(DEV_CONFIG_ROLE) { - _updateWhitelistAddress(_address, _isWhitelisted); - } - - function setContractURI(string memory _contractURI) public onlyRole(DEV_CONFIG_ROLE) { - contractURI = _contractURI; - emit ContractURIChanged(_contractURI); - } - - function adminVerifySignature( - address to, - uint256 nonce, - bytes calldata data, - bytes calldata signature - ) public onlyRole(DEV_CONFIG_ROLE) returns (bool) { - return _verifySignature(to, nonce, data, signature); - } - - function addWhitelistSigner(address _signer) external onlyRole(DEV_CONFIG_ROLE) { - _addWhitelistSigner(_signer); - } - - function removeWhitelistSigner(address signer) external onlyRole(DEV_CONFIG_ROLE) { - _removeWhitelistSigner(signer); - } - - // Reserved storage space to allow for layout changes in the future. - uint256[37] private __gap; -} From 74efc6fbc3bb4b1c6937fcca6a4076cbb4d3c687 Mon Sep 17 00:00:00 2001 From: Max Vasin Limsukhawat Date: Wed, 22 May 2024 14:03:03 -0600 Subject: [PATCH 2/2] test: fix failed forge tests --- constants/contract.ts | 2 +- .../deployments/deployments-base-sepolia.ts | 62 ++- contracts/libraries/LibItems.sol | 5 + .../soulbounds/ERC1155RoyaltiesSoulbound.sol | 68 +-- contracts/soulbounds/ERC1155Soulbound.sol | 67 +-- .../ERC1155RoyaltiesSoulboundV2.sol | 480 ++++++++++++++++++ .../soulbounds/ERC1155SoulboundV1.sol | 10 +- test/AvatarBound.t.sol | 18 +- test/AvatarBoundV1.t.sol | 18 +- test/ItemBound.t.sol | 49 +- test/ItemBoundV1.t.sol | 76 ++- 11 files changed, 695 insertions(+), 160 deletions(-) create mode 100644 contracts/upgradeables/soulbounds/ERC1155RoyaltiesSoulboundV2.sol diff --git a/constants/contract.ts b/constants/contract.ts index 97d58c2d..ee806ef1 100644 --- a/constants/contract.ts +++ b/constants/contract.ts @@ -68,7 +68,7 @@ export enum CONTRACT_FILE_NAME { export enum CONTRACT_UPGRADABLE_FILE_NAME { LegacyAvatar = 'LegacyAvatarUpgradeableV1', Avatars = 'AvatarBoundV1', - ERC1155RoyaltiesSoulbound = 'ERC1155RoyaltiesSoulboundV1', + ERC1155RoyaltiesSoulbound = 'ERC1155RoyaltiesSoulboundV2', Levels = 'LevelsBoundV1', ERC20ChainlinkPaymaster = 'ERC20ChainlinkPaymasterV2', ERC1155Soulbound = 'ERC1155SoulboundV1', diff --git a/constants/deployments/deployments-base-sepolia.ts b/constants/deployments/deployments-base-sepolia.ts index d92443b5..c0ea05ad 100644 --- a/constants/deployments/deployments-base-sepolia.ts +++ b/constants/deployments/deployments-base-sepolia.ts @@ -1,4 +1,4 @@ -import { BUIDLArgs, ERC20Args, ERC20DecimalsAgs } from '@constants/constructor-args'; +import { AvatarBoundV1Args, ERC20DecimalsAgs, ItemBoundArgs, LevelsBoundV1Args } from '@constants/constructor-args'; import { CONTRACT_TYPE, CONTRACT_UPGRADABLE_FILE_NAME, CONTRACT_UPGRADABLE_NAME } from '@constants/contract'; import { TENANT } from '@constants/tenant'; @@ -22,4 +22,64 @@ export const BASE_SEPOLIA_CONTRACTS: DeploymentContract[] = [ functionCalls: [], args: ERC20DecimalsAgs.TESTNET, }, + { + contractFileName: CONTRACT_UPGRADABLE_FILE_NAME.Levels, + type: CONTRACT_TYPE.Levels, + name: CONTRACT_UPGRADABLE_NAME.Levels, + chain, + networkType, + tenants: [TENANT.Game7], + verify: true, + upgradable: true, + dependencies: [CONTRACT_UPGRADABLE_NAME.Items, CONTRACT_UPGRADABLE_NAME.Avatars], + args: LevelsBoundV1Args.TESTNET, + }, + { + contractFileName: CONTRACT_UPGRADABLE_FILE_NAME.Avatars, + type: CONTRACT_TYPE.Avatars, + name: CONTRACT_UPGRADABLE_NAME.Avatars, + chain, + networkType, + tenants: [TENANT.Game7], + verify: true, + upgradable: true, + dependencies: [CONTRACT_UPGRADABLE_NAME.Items, CONTRACT_UPGRADABLE_NAME.Levels], + functionCalls: [ + { + contractName: CONTRACT_UPGRADABLE_NAME.Avatars, + functionName: 'setDefaultItemId', + args: [10001], + }, + { + contractName: CONTRACT_UPGRADABLE_NAME.Avatars, + functionName: 'setSpecialItemId', + args: [10002], + }, + ], + args: AvatarBoundV1Args.TESTNET, + }, + { + contractFileName: CONTRACT_UPGRADABLE_FILE_NAME.ERC1155RoyaltiesSoulbound, + type: CONTRACT_TYPE.Items, + name: CONTRACT_UPGRADABLE_NAME.Items, + chain, + networkType, + tenants: [TENANT.Game7], + verify: true, + upgradable: true, + dependencies: [CONTRACT_UPGRADABLE_NAME.Avatars], + functionCalls: [ + { + contractName: CONTRACT_UPGRADABLE_NAME.Items, + functionName: 'grantRole', + args: ['MINTER_ROLE', `CONTRACT_${CONTRACT_UPGRADABLE_NAME.Avatars}`], + }, + { + contractName: CONTRACT_UPGRADABLE_NAME.Items, + functionName: 'grantRole', + args: ['MINTER_ROLE', `CONTRACT_${CONTRACT_UPGRADABLE_NAME.Levels}`], + }, + ], + args: ItemBoundArgs.TESTNET, + }, ]; diff --git a/contracts/libraries/LibItems.sol b/contracts/libraries/LibItems.sol index 0b7f258c..ae20d2aa 100644 --- a/contracts/libraries/LibItems.sol +++ b/contracts/libraries/LibItems.sol @@ -52,6 +52,11 @@ library LibItems { uint256 maxSupply; // 0 mean unlimited } + struct TokenCreateLegacy { + uint256 tokenId; + string tokenUri; + } + struct TokenCreate { uint256 tokenId; string tokenUri; diff --git a/contracts/soulbounds/ERC1155RoyaltiesSoulbound.sol b/contracts/soulbounds/ERC1155RoyaltiesSoulbound.sol index 542e655a..86a577d2 100644 --- a/contracts/soulbounds/ERC1155RoyaltiesSoulbound.sol +++ b/contracts/soulbounds/ERC1155RoyaltiesSoulbound.sol @@ -35,6 +35,7 @@ import { ERCWhitelistSignature } from "../ercs/ERCWhitelistSignature.sol"; import { LibItems } from "../libraries/LibItems.sol"; error InvalidSeed(); +error MissingRole(); contract ERC1155RoyaltiesSoulbound is ERC1155Burnable, @@ -105,16 +106,20 @@ contract ERC1155RoyaltiesSoulbound is if (_isPaused) _pause(); } - function getAllItems() public view returns (LibItems.TokenReturn[] memory) { + function getAllItems(address _owner) public view returns (LibItems.TokenReturn[] memory) { + bool isAdmin = hasRole(MINTER_ROLE, _msgSender()); + if (!isAdmin && _owner != _msgSender()) { + revert MissingRole(); + } uint256 totalTokens = itemIds.length; LibItems.TokenReturn[] memory tokenReturns = new LibItems.TokenReturn[](totalTokens); uint index; for (uint i = 0; i < totalTokens; i++) { uint256 tokenId = itemIds[i]; - uint256 amount = balanceOf(_msgSender(), tokenId); + uint256 amount = balanceOf(_owner, tokenId); - if (amount > 0) { + if (isAdmin || amount > 0) { LibItems.TokenReturn memory tokenReturn = LibItems.TokenReturn({ tokenId: tokenId, tokenUri: uri(tokenId), @@ -134,35 +139,6 @@ contract ERC1155RoyaltiesSoulbound is return returnsTruncated; } - function getAllItemsAdmin( - address _owner - ) public view onlyRole(MINTER_ROLE) returns (LibItems.TokenReturn[] memory) { - uint256 totalTokens = itemIds.length; - LibItems.TokenReturn[] memory tokenReturns = new LibItems.TokenReturn[](totalTokens); - - uint index; - for (uint i = 0; i < totalTokens; i++) { - uint256 tokenId = itemIds[i]; - uint256 amount = balanceOf(_owner, tokenId); - - LibItems.TokenReturn memory tokenReturn = LibItems.TokenReturn({ - tokenId: tokenId, - tokenUri: uri(tokenId), - amount: amount - }); - tokenReturns[index] = tokenReturn; - index++; - } - - // truncate the array - LibItems.TokenReturn[] memory returnsTruncated = new LibItems.TokenReturn[](index); - for (uint i = 0; i < index; i++) { - returnsTruncated[i] = tokenReturns[i]; - } - - return returnsTruncated; - } - function isTokenExist(uint256 _tokenId) public view returns (bool) { if (!tokenExists[_tokenId]) { revert("TokenNotExist"); @@ -215,6 +191,10 @@ contract ERC1155RoyaltiesSoulbound is tokenUris[_token.tokenId] = _token.tokenUri; } + if (_token.receiver != address(0)) { + _setTokenRoyalty(_token.tokenId, _token.receiver, uint96(_token.feeBasisPoints)); + } + tokenExists[_token.tokenId] = true; itemIds.push(_token.tokenId); @@ -227,30 +207,6 @@ contract ERC1155RoyaltiesSoulbound is } } - function addNewTokenWithRoyalty(LibItems.TokenCreateWithRoyalty calldata _token) public onlyRole(DEV_CONFIG_ROLE) { - if (_token.receiver == address(0)) { - revert("ReceiverAddressZero"); - } - - if (bytes(_token.tokenUri).length > 0) { - tokenUris[_token.tokenId] = _token.tokenUri; - } - - tokenExists[_token.tokenId] = true; - - itemIds.push(_token.tokenId); - - _setTokenRoyalty(_token.tokenId, _token.receiver, uint96(_token.feeBasisPoints)); - } - - function addNewTokensWithRoyalty( - LibItems.TokenCreateWithRoyalty[] calldata _tokens - ) external onlyRole(DEV_CONFIG_ROLE) { - for (uint256 i = 0; i < _tokens.length; i++) { - addNewTokenWithRoyalty(_tokens[i]); - } - } - function updateTokenUri(uint256 _tokenId, string calldata _tokenUri) public onlyRole(DEV_CONFIG_ROLE) { tokenUris[_tokenId] = _tokenUri; } diff --git a/contracts/soulbounds/ERC1155Soulbound.sol b/contracts/soulbounds/ERC1155Soulbound.sol index a6d34e97..bb9fdca5 100644 --- a/contracts/soulbounds/ERC1155Soulbound.sol +++ b/contracts/soulbounds/ERC1155Soulbound.sol @@ -34,6 +34,8 @@ import { Achievo1155Soulbound } from "../ercs/extensions/Achievo1155Soulbound.so import { ERCWhitelistSignature } from "../ercs/ERCWhitelistSignature.sol"; import { LibItems } from "../libraries/LibItems.sol"; +error MissingRole(); + contract ERC1155Soulbound is ERC1155Burnable, ERC1155Supply, @@ -103,16 +105,20 @@ contract ERC1155Soulbound is if (_isPaused) _pause(); } - function getAllItems() public view returns (LibItems.TokenReturn[] memory) { + function getAllItems(address _owner) public view returns (LibItems.TokenReturn[] memory) { + bool isAdmin = hasRole(MINTER_ROLE, _msgSender()); + if (!isAdmin && _owner != _msgSender()) { + revert MissingRole(); + } uint256 totalTokens = itemIds.length; LibItems.TokenReturn[] memory tokenReturns = new LibItems.TokenReturn[](totalTokens); uint index; for (uint i = 0; i < totalTokens; i++) { uint256 tokenId = itemIds[i]; - uint256 amount = balanceOf(_msgSender(), tokenId); + uint256 amount = balanceOf(_owner, tokenId); - if (amount > 0) { + if (isAdmin || amount > 0) { LibItems.TokenReturn memory tokenReturn = LibItems.TokenReturn({ tokenId: tokenId, tokenUri: uri(tokenId), @@ -132,35 +138,6 @@ contract ERC1155Soulbound is return returnsTruncated; } - function getAllItemsAdmin( - address _owner - ) public view onlyRole(MINTER_ROLE) returns (LibItems.TokenReturn[] memory) { - uint256 totalTokens = itemIds.length; - LibItems.TokenReturn[] memory tokenReturns = new LibItems.TokenReturn[](totalTokens); - - uint index; - for (uint i = 0; i < totalTokens; i++) { - uint256 tokenId = itemIds[i]; - uint256 amount = balanceOf(_owner, tokenId); - - LibItems.TokenReturn memory tokenReturn = LibItems.TokenReturn({ - tokenId: tokenId, - tokenUri: uri(tokenId), - amount: amount - }); - tokenReturns[index] = tokenReturn; - index++; - } - - // truncate the array - LibItems.TokenReturn[] memory returnsTruncated = new LibItems.TokenReturn[](index); - for (uint i = 0; i < index; i++) { - returnsTruncated[i] = tokenReturns[i]; - } - - return returnsTruncated; - } - function isTokenExist(uint256 _tokenId) public view returns (bool) { if (!tokenExists[_tokenId]) { revert("TokenNotExist"); @@ -185,25 +162,13 @@ contract ERC1155Soulbound is _unpause(); } - function addNewTokenWithRoyalty(LibItems.TokenCreateWithRoyalty calldata _token) public onlyRole(DEV_CONFIG_ROLE) { - if (_token.receiver == address(0)) { - revert("ReceiverAddressZero"); - } - + function addNewToken(LibItems.TokenCreate calldata _token) public onlyRole(DEV_CONFIG_ROLE) { if (bytes(_token.tokenUri).length > 0) { tokenUris[_token.tokenId] = _token.tokenUri; } - tokenExists[_token.tokenId] = true; - - itemIds.push(_token.tokenId); - - _setTokenRoyalty(_token.tokenId, _token.receiver, uint96(_token.feeBasisPoints)); - } - - function addNewToken(LibItems.TokenCreate calldata _token) public onlyRole(DEV_CONFIG_ROLE) { - if (bytes(_token.tokenUri).length > 0) { - tokenUris[_token.tokenId] = _token.tokenUri; + if (_token.receiver != address(0)) { + _setTokenRoyalty(_token.tokenId, _token.receiver, uint96(_token.feeBasisPoints)); } tokenExists[_token.tokenId] = true; @@ -212,14 +177,6 @@ contract ERC1155Soulbound is emit TokenAdded(_token.tokenId); } - function addNewTokensWithRoyalty( - LibItems.TokenCreateWithRoyalty[] calldata _tokens - ) external onlyRole(DEV_CONFIG_ROLE) { - for (uint256 i = 0; i < _tokens.length; i++) { - addNewTokenWithRoyalty(_tokens[i]); - } - } - function addNewTokens(LibItems.TokenCreate[] calldata _tokens) external onlyRole(DEV_CONFIG_ROLE) { for (uint256 i = 0; i < _tokens.length; i++) { addNewToken(_tokens[i]); diff --git a/contracts/upgradeables/soulbounds/ERC1155RoyaltiesSoulboundV2.sol b/contracts/upgradeables/soulbounds/ERC1155RoyaltiesSoulboundV2.sol new file mode 100644 index 00000000..d7b56c5f --- /dev/null +++ b/contracts/upgradeables/soulbounds/ERC1155RoyaltiesSoulboundV2.sol @@ -0,0 +1,480 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +/** + * Author: Achievo Team - (https://achievo.xyz/) + */ + +//TODO: This contract is deprecated USE THE ERC1155SoulboundV1.sol + +// MMMMNkc. .,oKWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM +// MWXd,. .cONMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM +// Wx' .cKMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM +// x. ;KMMMMMMMMMMMMWKxlcclxKWMMMMMMMMWKxlc::::::::ckWMMXd:cOMMMMMMMMKo:oKMMWkccxWMWKdccccccccccccoKMM0l:l0MMMMMMMMMWkc:dXMMMXkoc::::::clxKW +// ' lNMMMMMMMMMMNd. .. .dNMMMMMMNd. ..........oWMM0' oWMMMMMMMk. .kMMN: :XMNl .''''''''';OMMX: ,0MMMMMMMWk. oNMWk' ........ .o +// . :XMMMMMMMMMWd. .o00l. .dWMMMMWx. .o0KKXKKXXXXNMMM0' oNWWWWWWWk. .kMMN: :NMNc .kNNNNNNNNNNWMMM0, :XMMMMMM0, cXMMO. c0KKKKXK0o. +// , .lkxo. ;dkx, oWMMMMMMMMWk. oNMMNo. .kWMMMWl ;KMMMMMMMMMMMMMM0' .',',,,,,. .kMMN: :NMNc ,:;;;;;;dXMMMMMMO. lNMMMMK: ;KMMMd. .OMMMMMMMMX; +// : :KWX: .xMWx. .kMMMMMMMMM0' cXMMMMXc ,0MMMWl ;KMMMMMMMMMMMMMM0' .',,'',,,. .kMMN: :NMNc ',,;;,;;oXMMMMMMWx. .dWMMNc 'OMMMMd. .OMMMMMMMMX; +// l ,0WO:oXWd. .OMMMMMMMMK; ;KMMMMMMK; :KMMWd. .o0KKXXKKKXXNMMM0' oNWWWWWWWx. .kMMN: :XMNc .kNNNNNNNNWWWMMMMMNo. .dK0l. .xWMMMMO. .c0KKKXXK0o. +// o dWMWWMK, '0MMMMMMMXc 'OMMMMMMMMO' cNMMNd. ..........oWMM0' oWMMMMMMMk. .kMMN: :XMNl .,,,,,,,,,:0MMMMMMNo. .. 'xWMMMMMWx' ....... .o +// O' :XMMMMk. cXMMMMMMMKo:cOWMMMMMMMMWOc:oKMMMWKxlc::::::::ckWMMXd:cOMMMMMMMMKo:oKMMWkc:xWMWKoc:::::::::::lKMMMMMMMWKdlcclxXWMMMMMMMMXkoc::::::clxKW +// WO; 'OMMMWl .oXMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM +// MMNx'.dWMMK;.:0WMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM +// MMMM0cdNMM0cdNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM + +import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import { ERC1155Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC1155/ERC1155Upgradeable.sol"; +import { + ERC1155BurnableUpgradeable +} from "@openzeppelin/contracts-upgradeable/token/ERC1155/extensions/ERC1155BurnableUpgradeable.sol"; +import { + ERC1155SupplyUpgradeable +} from "@openzeppelin/contracts-upgradeable/token/ERC1155/extensions/ERC1155SupplyUpgradeable.sol"; +import { AccessControlUpgradeable } from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; +import { PausableUpgradeable } from "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol"; +import { StringsUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; +import { ERC2981Upgradeable } from "@openzeppelin/contracts-upgradeable/token/common/ERC2981Upgradeable.sol"; +import { ECDSAUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol"; +import { + ReentrancyGuardUpgradeable +} from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; + +import { Achievo1155SoulboundUpgradeable } from "../ercs/extensions/Achievo1155SoulboundUpgradeable.sol"; +import { ERCWhitelistSignatureUpgradeable } from "../ercs/ERCWhitelistSignatureUpgradeable.sol"; +import { LibItems } from "../../libraries/LibItems.sol"; + +error InvalidSeed(); +error InvalidInput(); +error AddressIsZero(); +error ExceedMaxMint(); +error MissingRole(); +error TokenNotExist(); +error TokenMintPaused(); +error DuplicateID(); + +contract ERC1155RoyaltiesSoulboundV2 is + Initializable, + ERC1155BurnableUpgradeable, + ERC1155SupplyUpgradeable, + Achievo1155SoulboundUpgradeable, + ERC2981Upgradeable, + ERCWhitelistSignatureUpgradeable, + AccessControlUpgradeable, + PausableUpgradeable, + ReentrancyGuardUpgradeable +{ + event ContractURIChanged(string indexed uri); + + bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); + bytes32 public constant MANAGER_ROLE = keccak256("MANAGER_ROLE"); + bytes32 public constant DEV_CONFIG_ROLE = keccak256("DEV_CONFIG_ROLE"); + + string public contractURI; + string private baseURI; + string public name; + string public symbol; + using StringsUpgradeable for uint256; + + uint256 public MAX_PER_MINT; + + mapping(uint256 => bool) private tokenExists; + mapping(uint256 => string) public tokenUris; // tokenId => tokenUri + mapping(uint256 => bool) public isTokenMintPaused; // tokenId => bool - default is false + + uint256[] public itemIds; + + mapping(address => mapping(uint256 => bool)) private tokenIdProcessed; + + modifier maxPerMintCheck(uint256 amount) { + if (amount > MAX_PER_MINT) { + revert ExceedMaxMint(); + } + _; + } + + event Minted(address indexed to, uint256[] tokenIds, uint256 amount, bool soulbound); + event MintedId(address indexed to, uint256 indexed tokenId, uint256 amount, bool soulbound); + event TokenAdded(uint256 indexed tokenId); + + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + + function initialize( + string memory _name, + string memory _symbol, + string memory _initBaseURI, + string memory _contractURI, + uint256 _maxPerMint, + bool _isPaused, + address devWallet + ) public initializer { + __ERC1155_init(""); + __ReentrancyGuard_init(); + __AccessControl_init(); + __Achievo1155SoulboundUpgradable_init(); + __ERCWhitelistSignatureUpgradeable_init(); + + if (devWallet == address(0)) { + revert AddressIsZero(); + } + + _grantRole(DEFAULT_ADMIN_ROLE, devWallet); + _grantRole(MINTER_ROLE, devWallet); + _grantRole(MANAGER_ROLE, devWallet); + _grantRole(DEV_CONFIG_ROLE, devWallet); + _addWhitelistSigner(devWallet); + + name = _name; + symbol = _symbol; + baseURI = _initBaseURI; + contractURI = _contractURI; + MAX_PER_MINT = _maxPerMint; + + if (_isPaused) _pause(); + } + + function getAllItems(address _owner) public view returns (LibItems.TokenReturn[] memory) { + bool isAdmin = hasRole(MINTER_ROLE, _msgSender()); + if (!isAdmin && _owner != _msgSender()) { + revert MissingRole(); + } + uint256 totalTokens = itemIds.length; + LibItems.TokenReturn[] memory tokenReturns = new LibItems.TokenReturn[](totalTokens); + + uint index; + for (uint i = 0; i < totalTokens; i++) { + uint256 tokenId = itemIds[i]; + uint256 amount = balanceOf(_owner, tokenId); + + if (isAdmin || amount > 0) { + LibItems.TokenReturn memory tokenReturn = LibItems.TokenReturn({ + tokenId: tokenId, + tokenUri: uri(tokenId), + amount: amount + }); + tokenReturns[index] = tokenReturn; + index++; + } + } + + // truncate the array + LibItems.TokenReturn[] memory returnsTruncated = new LibItems.TokenReturn[](index); + for (uint i = 0; i < index; i++) { + returnsTruncated[i] = tokenReturns[i]; + } + + return returnsTruncated; + } + + function isTokenExist(uint256 _tokenId) public view returns (bool) { + if (!tokenExists[_tokenId]) { + revert TokenNotExist(); + } + return true; + } + + function getChainID() public view returns (uint256) { + uint256 id; + assembly { + id := chainid() + } + return id; + } + + function _verifyContractChainIdAndDecode(bytes calldata data) private view returns (uint256[] memory) { + uint256 currentChainId = getChainID(); + (address contractAddress, uint256 chainId, uint256[] memory tokenIds) = _decodeData(data); + + if (chainId != currentChainId || contractAddress != address(this)) { + revert InvalidSeed(); + } + return tokenIds; + } + + function decodeData( + bytes calldata _data + ) public view onlyRole(DEV_CONFIG_ROLE) returns (address, uint256, uint256[] memory) { + return _decodeData(_data); + } + + function _decodeData(bytes calldata _data) private view returns (address, uint256, uint256[] memory) { + (address contractAddress, uint256 chainId, uint256[] memory _itemIds) = abi.decode( + _data, + (address, uint256, uint256[]) + ); + return (contractAddress, chainId, _itemIds); + } + + function pause() external onlyRole(MANAGER_ROLE) { + _pause(); + } + + function unpause() external onlyRole(MANAGER_ROLE) { + _unpause(); + } + + function addNewToken(LibItems.TokenCreate calldata _token) public onlyRole(DEV_CONFIG_ROLE) { + if (bytes(_token.tokenUri).length > 0) { + tokenUris[_token.tokenId] = _token.tokenUri; + } + + if (_token.receiver != address(0)) { + _setTokenRoyalty(_token.tokenId, _token.receiver, uint96(_token.feeBasisPoints)); + } + + tokenExists[_token.tokenId] = true; + + itemIds.push(_token.tokenId); + emit TokenAdded(_token.tokenId); + } + + function addNewTokens(LibItems.TokenCreate[] calldata _tokens) external onlyRole(DEV_CONFIG_ROLE) { + for (uint256 i = 0; i < _tokens.length; i++) { + addNewToken(_tokens[i]); + } + } + + function updateTokenUri(uint256 _tokenId, string calldata _tokenUri) public onlyRole(DEV_CONFIG_ROLE) { + tokenUris[_tokenId] = _tokenUri; + } + + function batchUpdateTokenUri( + uint256[] calldata _tokenIds, + string[] calldata _tokenUris + ) public onlyRole(DEV_CONFIG_ROLE) { + if (_tokenIds.length != _tokenUris.length) { + revert InvalidInput(); + } + for (uint256 i = 0; i < _tokenIds.length; i++) { + updateTokenUri(_tokenIds[i], _tokenUris[i]); + } + } + + function updateTokenMintPaused(uint256 _tokenId, bool _isTokenMintPaused) public onlyRole(MANAGER_ROLE) { + isTokenMintPaused[_tokenId] = _isTokenMintPaused; + } + + function _mintBatch(address to, uint256[] memory _tokenIds, uint256 amount, bool soulbound) private { + for (uint256 i = 0; i < _tokenIds.length; i++) { + uint256 _id = _tokenIds[i]; + isTokenExist(_id); + if (isTokenMintPaused[_id]) { + revert TokenMintPaused(); + } + + if (soulbound) { + _soulbound(to, _id, amount); + } + + _mint(to, _id, amount, ""); + } + emit Minted(to, _tokenIds, amount, soulbound); + } + + function mint( + bytes calldata data, + uint256 amount, + bool soulbound, + uint256 nonce, + bytes calldata signature + ) external nonReentrant signatureCheck(_msgSender(), nonce, data, signature) maxPerMintCheck(amount) whenNotPaused { + uint256[] memory _tokenIds = _verifyContractChainIdAndDecode(data); + _mintBatch(_msgSender(), _tokenIds, amount, soulbound); + } + + function adminMint(address to, bytes calldata data, bool soulbound) external onlyRole(MINTER_ROLE) whenNotPaused { + uint256[] memory _tokenIds = _verifyContractChainIdAndDecode(data); + _mintBatch(to, _tokenIds, 1, soulbound); + } + + function adminMintId( + address to, + uint256 id, + uint256 amount, + bool soulbound + ) external onlyRole(MINTER_ROLE) whenNotPaused { + isTokenExist(id); + + if (isTokenMintPaused[id]) { + revert TokenMintPaused(); + } + + if (soulbound) { + _soulbound(to, id, amount); + } + + _mint(to, id, amount, ""); + emit MintedId(to, id, amount, soulbound); + } + + function _beforeTokenTransfer( + address operator, + address from, + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) internal virtual override(ERC1155Upgradeable, ERC1155SupplyUpgradeable) { + super._beforeTokenTransfer(operator, from, to, ids, amounts, data); + } + + function safeTransferFrom( + address _from, + address _to, + uint256 _id, + uint256 _amount, + bytes memory _data + ) public virtual override soulboundCheckAndSync(_from, _to, _id, _amount, balanceOf(_from, _id)) { + super.safeTransferFrom(_from, _to, _id, _amount, _data); + } + + function safeBatchTransferFrom( + address _from, + address _to, + uint256[] memory _ids, + uint256[] memory _amounts, + bytes memory _data + ) + public + virtual + override + soulboundCheckAndSyncBatch(_from, _to, _ids, _amounts, balanceOfBatchOneAccount(_from, _ids)) + { + for (uint256 i = 0; i < _ids.length; i++) { + uint256 id = _ids[i]; + + if (tokenIdProcessed[_from][id]) { + revert DuplicateID(); + } + + tokenIdProcessed[_from][id] = true; + } + + super.safeBatchTransferFrom(_from, _to, _ids, _amounts, _data); + + // Reset processed status after the transfer is completed + for (uint256 i = 0; i < _ids.length; i++) { + uint256 id = _ids[i]; + tokenIdProcessed[_from][id] = false; + } + } + + function balanceOfBatchOneAccount( + address account, + uint256[] memory ids + ) public view virtual returns (uint256[] memory) { + uint256[] memory batchBalances = new uint256[](ids.length); + + for (uint256 i = 0; i < ids.length; ++i) { + batchBalances[i] = balanceOf(account, ids[i]); + } + + return batchBalances; + } + + function burn( + address to, + uint256 tokenId, + uint256 amount + ) + public + virtual + override + nonReentrant + soulboundCheckAndSync(to, address(0), tokenId, amount, balanceOf(to, tokenId)) + { + ERC1155BurnableUpgradeable.burn(to, tokenId, amount); + } + + function burnBatch( + address to, + uint256[] memory tokenIds, + uint256[] memory amounts + ) + public + virtual + override + nonReentrant + soulboundCheckAndSyncBatch(to, address(0), tokenIds, amounts, balanceOfBatchOneAccount(to, tokenIds)) + { + for (uint256 i = 0; i < tokenIds.length; i++) { + uint256 id = tokenIds[i]; + + if (tokenIdProcessed[to][id]) { + revert DuplicateID(); + } + + tokenIdProcessed[to][id] = true; + } + + ERC1155BurnableUpgradeable.burnBatch(to, tokenIds, amounts); + + // Reset processed status after the transfer is completed + for (uint256 i = 0; i < tokenIds.length; i++) { + uint256 id = tokenIds[i]; + tokenIdProcessed[to][id] = false; + } + } + + function supportsInterface( + bytes4 interfaceId + ) public view override(ERC1155Upgradeable, ERC2981Upgradeable, AccessControlUpgradeable) returns (bool) { + return super.supportsInterface(interfaceId); + } + + function uri(uint256 tokenId) public view override returns (string memory) { + isTokenExist(tokenId); + if (bytes(tokenUris[tokenId]).length > 0) { + return tokenUris[tokenId]; + } else { + return string(abi.encodePacked(baseURI, "/", tokenId.toString())); + } + } + + function updateBaseUri(string memory _baseURI) external onlyRole(DEV_CONFIG_ROLE) { + baseURI = _baseURI; + } + + function setRoyaltyInfo(address receiver, uint96 feeBasisPoints) external onlyRole(MANAGER_ROLE) { + _setDefaultRoyalty(receiver, feeBasisPoints); + } + + function setTokenRoyalty(uint256 tokenId, address receiver, uint96 feeBasisPoints) external onlyRole(MANAGER_ROLE) { + _setTokenRoyalty(tokenId, receiver, uint96(feeBasisPoints)); + } + + function resetTokenRoyalty(uint256 tokenId) external onlyRole(MANAGER_ROLE) { + _resetTokenRoyalty(tokenId); + } + + function updateWhitelistAddress(address _address, bool _isWhitelisted) external onlyRole(DEV_CONFIG_ROLE) { + _updateWhitelistAddress(_address, _isWhitelisted); + } + + function setContractURI(string memory _contractURI) public onlyRole(DEV_CONFIG_ROLE) { + contractURI = _contractURI; + emit ContractURIChanged(_contractURI); + } + + function adminVerifySignature( + address to, + uint256 nonce, + bytes calldata data, + bytes calldata signature + ) public onlyRole(DEV_CONFIG_ROLE) returns (bool) { + return _verifySignature(to, nonce, data, signature); + } + + function addWhitelistSigner(address _signer) external onlyRole(DEV_CONFIG_ROLE) { + _addWhitelistSigner(_signer); + } + + function removeWhitelistSigner(address signer) external onlyRole(DEV_CONFIG_ROLE) { + _removeWhitelistSigner(signer); + } + + // Reserved storage space to allow for layout changes in the future. + uint256[37] private __gap; +} diff --git a/contracts/upgradeables/soulbounds/ERC1155SoulboundV1.sol b/contracts/upgradeables/soulbounds/ERC1155SoulboundV1.sol index 6901f4cc..fc2f8ff7 100644 --- a/contracts/upgradeables/soulbounds/ERC1155SoulboundV1.sol +++ b/contracts/upgradeables/soulbounds/ERC1155SoulboundV1.sol @@ -206,7 +206,7 @@ contract ERC1155SoulboundV1 is _unpause(); } - function addNewToken(LibItems.TokenCreate calldata _token) public onlyRole(DEV_CONFIG_ROLE) { + function addNewToken(LibItems.TokenCreateLegacy calldata _token) public onlyRole(DEV_CONFIG_ROLE) { if (bytes(_token.tokenUri).length > 0) { tokenUris[_token.tokenId] = _token.tokenUri; } @@ -217,7 +217,7 @@ contract ERC1155SoulboundV1 is emit TokenAdded(_token.tokenId); } - function addNewTokens(LibItems.TokenCreate[] calldata _tokens) external onlyRole(DEV_CONFIG_ROLE) { + function addNewTokens(LibItems.TokenCreateLegacy[] calldata _tokens) external onlyRole(DEV_CONFIG_ROLE) { for (uint256 i = 0; i < _tokens.length; i++) { addNewToken(_tokens[i]); } @@ -450,7 +450,7 @@ contract ERC1155SoulboundV1 is _removeWhitelistSigner(signer); } - function addNewTokenWithRoyalty(LibItems.TokenCreateWithRoyalty calldata _token) public onlyRole(DEV_CONFIG_ROLE) { + function addNewTokenWithRoyalty(LibItems.TokenCreate calldata _token) public onlyRole(DEV_CONFIG_ROLE) { if (_token.receiver == address(0)) { revert("ReceiverAddressZero"); } @@ -466,9 +466,7 @@ contract ERC1155SoulboundV1 is _setTokenRoyalty(_token.tokenId, _token.receiver, uint96(_token.feeBasisPoints)); } - function addNewTokensWithRoyalty( - LibItems.TokenCreateWithRoyalty[] calldata _tokens - ) external onlyRole(DEV_CONFIG_ROLE) { + function addNewTokensWithRoyalty(LibItems.TokenCreate[] calldata _tokens) external onlyRole(DEV_CONFIG_ROLE) { for (uint256 i = 0; i < _tokens.length; i++) { addNewTokenWithRoyalty(_tokens[i]); } diff --git a/test/AvatarBound.t.sol b/test/AvatarBound.t.sol index aee85652..c6577ef8 100644 --- a/test/AvatarBound.t.sol +++ b/test/AvatarBound.t.sol @@ -102,7 +102,9 @@ contract AvatarBoundTest is Test { LibItems.TokenCreate memory _token = LibItems.TokenCreate({ tokenId: _tokenId, - tokenUri: string(abi.encodePacked("https://something.com", "/", _tokenId.toString())) + tokenUri: string(abi.encodePacked("https://something.com", "/", _tokenId.toString())), + receiver: address(0), + feeBasisPoints: 0 }); _tokens.push(_token); @@ -110,11 +112,21 @@ contract AvatarBoundTest is Test { _tokenItemsIds.push(_tokenId); } - LibItems.TokenCreate memory defaultItem = LibItems.TokenCreate({ tokenId: defaultItemId, tokenUri: "" }); + LibItems.TokenCreate memory defaultItem = LibItems.TokenCreate({ + tokenId: defaultItemId, + tokenUri: "", + receiver: address(0), + feeBasisPoints: 0 + }); _tokens.push(defaultItem); - LibItems.TokenCreate memory specialItem = LibItems.TokenCreate({ tokenId: specialItemId, tokenUri: "" }); + LibItems.TokenCreate memory specialItem = LibItems.TokenCreate({ + tokenId: specialItemId, + tokenUri: "", + receiver: address(0), + feeBasisPoints: 0 + }); _tokens.push(specialItem); diff --git a/test/AvatarBoundV1.t.sol b/test/AvatarBoundV1.t.sol index 6e1991bc..57612235 100644 --- a/test/AvatarBoundV1.t.sol +++ b/test/AvatarBoundV1.t.sol @@ -111,7 +111,9 @@ contract AvatarBoundV1Test is StdCheats, Test { LibItems.TokenCreate memory _token = LibItems.TokenCreate({ tokenId: _tokenId, - tokenUri: string(abi.encodePacked("https://something.com", "/", _tokenId.toString())) + tokenUri: string(abi.encodePacked("https://something.com", "/", _tokenId.toString())), + receiver: address(0), + feeBasisPoints: 0 }); _tokens.push(_token); @@ -119,11 +121,21 @@ contract AvatarBoundV1Test is StdCheats, Test { _tokenItemsIds.push(_tokenId); } - LibItems.TokenCreate memory defaultItem = LibItems.TokenCreate({ tokenId: defaultItemId, tokenUri: "" }); + LibItems.TokenCreate memory defaultItem = LibItems.TokenCreate({ + tokenId: defaultItemId, + tokenUri: "", + receiver: address(0), + feeBasisPoints: 0 + }); _tokens.push(defaultItem); - LibItems.TokenCreate memory specialItem = LibItems.TokenCreate({ tokenId: specialItemId, tokenUri: "" }); + LibItems.TokenCreate memory specialItem = LibItems.TokenCreate({ + tokenId: specialItemId, + tokenUri: "", + receiver: address(0), + feeBasisPoints: 0 + }); _tokens.push(specialItem); diff --git a/test/ItemBound.t.sol b/test/ItemBound.t.sol index ae158555..13cf4b5a 100644 --- a/test/ItemBound.t.sol +++ b/test/ItemBound.t.sol @@ -133,7 +133,9 @@ contract ItemBoundTest is StdCheats, Test { LibItems.TokenCreate memory _token = LibItems.TokenCreate({ tokenId: _tokenId, - tokenUri: string(abi.encodePacked("https://something.com", "/", _tokenId.toString())) + tokenUri: string(abi.encodePacked("https://something.com", "/", _tokenId.toString())), + receiver: address(0), + feeBasisPoints: 0 }); _tokens.push(_token); @@ -171,7 +173,9 @@ contract ItemBoundTest is StdCheats, Test { LibItems.TokenCreate memory _token = LibItems.TokenCreate({ tokenId: _tokenId, - tokenUri: string(abi.encodePacked("https://something222.com", "/", _tokenId.toString())) + tokenUri: string(abi.encodePacked("https://something222.com", "/", _tokenId.toString())), + receiver: address(0), + feeBasisPoints: 0 }); itemBound.addNewToken(_token); @@ -190,7 +194,9 @@ contract ItemBoundTest is StdCheats, Test { LibItems.TokenCreate memory _token = LibItems.TokenCreate({ tokenId: _tokenId, - tokenUri: string(abi.encodePacked("https://something.com", "/", _tokenId.toString())) + tokenUri: string(abi.encodePacked("https://something.com", "/", _tokenId.toString())), + receiver: address(0), + feeBasisPoints: 0 }); _tokens[i] = _token; @@ -514,7 +520,12 @@ contract ItemBoundTest is StdCheats, Test { uint256 _level = generateRandomLevel(); // level 1-10 TestLibItems.Tier _tier = generateRandomTier(); // tier 0-4 - LibItems.TokenCreate memory _token = LibItems.TokenCreate({ tokenId: _tokenId, tokenUri: "" }); + LibItems.TokenCreate memory _token = LibItems.TokenCreate({ + tokenId: _tokenId, + tokenUri: "", + receiver: address(0), + feeBasisPoints: 0 + }); itemBound.addNewToken(_token); @@ -528,7 +539,9 @@ contract ItemBoundTest is StdCheats, Test { LibItems.TokenCreate memory _token = LibItems.TokenCreate({ tokenId: _tokenId, - tokenUri: "ipfs://specific-token-uri.com" + tokenUri: "ipfs://specific-token-uri.com", + receiver: address(0), + feeBasisPoints: 0 }); itemBound.addNewToken(_token); @@ -551,7 +564,12 @@ contract ItemBoundTest is StdCheats, Test { uint256 _level = generateRandomLevel(); // level 1-10 TestLibItems.Tier _tier = generateRandomTier(); // tier 0-4 - LibItems.TokenCreate memory _token = LibItems.TokenCreate({ tokenId: _tokenId, tokenUri: "" }); + LibItems.TokenCreate memory _token = LibItems.TokenCreate({ + tokenId: _tokenId, + tokenUri: "", + receiver: address(0), + feeBasisPoints: 0 + }); itemBound.addNewToken(_token); @@ -580,7 +598,12 @@ contract ItemBoundTest is StdCheats, Test { uint256 _level = generateRandomLevel(); // level 1-10 TestLibItems.Tier _tier = generateRandomTier(); // tier 0-4 - LibItems.TokenCreate memory _token = LibItems.TokenCreate({ tokenId: _tokenId, tokenUri: "" }); + LibItems.TokenCreate memory _token = LibItems.TokenCreate({ + tokenId: _tokenId, + tokenUri: "", + receiver: address(0), + feeBasisPoints: 0 + }); itemBound.addNewToken(_token); @@ -716,14 +739,14 @@ contract ItemBoundTest is StdCheats, Test { assertEq(itemBound.uri(_tokenIds[23]), "https://something-new.com/232"); vm.prank(playerWallet.addr); - LibItems.TokenReturn[] memory allTokensInfo = itemBound.getAllItems(); + LibItems.TokenReturn[] memory allTokensInfo = itemBound.getAllItems(playerWallet.addr); assertEq(allTokensInfo.length, 1300); vm.prank(playerWallet.addr); itemBound.safeTransferFrom(playerWallet.addr, minterWallet.addr, _tokenIds[24], 1, ""); vm.prank(playerWallet.addr); - LibItems.TokenReturn[] memory allTokensInfo2 = itemBound.getAllItems(); + LibItems.TokenReturn[] memory allTokensInfo2 = itemBound.getAllItems(playerWallet.addr); assertEq(allTokensInfo2.length, 1299); for (uint256 i = 0; i < allTokensInfo.length; i++) { @@ -742,7 +765,7 @@ contract ItemBoundTest is StdCheats, Test { } vm.prank(minterWallet.addr); - LibItems.TokenReturn[] memory allTokensInfo3 = itemBound.getAllItems(); + LibItems.TokenReturn[] memory allTokensInfo3 = itemBound.getAllItems(minterWallet.addr); assertEq(allTokensInfo3.length, 1); } @@ -754,13 +777,13 @@ contract ItemBoundTest is StdCheats, Test { itemBound.updateTokenUri(_tokenIds[23], newTokenUri); assertEq(itemBound.uri(_tokenIds[23]), "https://something-new.com/232"); - LibItems.TokenReturn[] memory allTokensInfo = itemBound.getAllItemsAdmin(playerWallet.addr); + LibItems.TokenReturn[] memory allTokensInfo = itemBound.getAllItems(playerWallet.addr); assertEq(allTokensInfo.length, 1300); vm.prank(playerWallet.addr); itemBound.safeTransferFrom(playerWallet.addr, minterWallet.addr, _tokenIds[24], 1, ""); - LibItems.TokenReturn[] memory allTokensInfo2 = itemBound.getAllItemsAdmin(playerWallet.addr); + LibItems.TokenReturn[] memory allTokensInfo2 = itemBound.getAllItems(playerWallet.addr); assertEq(allTokensInfo2.length, 1300); for (uint256 i = 0; i < allTokensInfo.length; i++) { @@ -778,7 +801,7 @@ contract ItemBoundTest is StdCheats, Test { } } - LibItems.TokenReturn[] memory allTokensInfo3 = itemBound.getAllItemsAdmin(minterWallet.addr); + LibItems.TokenReturn[] memory allTokensInfo3 = itemBound.getAllItems(minterWallet.addr); assertEq(allTokensInfo3.length, 1300); } } diff --git a/test/ItemBoundV1.t.sol b/test/ItemBoundV1.t.sol index 46f170c0..802d823f 100644 --- a/test/ItemBoundV1.t.sol +++ b/test/ItemBoundV1.t.sol @@ -13,6 +13,15 @@ import { LibItems, TestLibItems } from "../contracts/libraries/LibItems.sol"; import { ERC1155RoyaltiesSoulboundV1 } from "../contracts/upgradeables/soulbounds/ERC1155RoyaltiesSoulboundV1.sol"; import { MockERC1155Receiver } from "../contracts/mocks/MockERC1155Receiver.sol"; +error InvalidSeed(); +error InvalidInput(); +error AddressIsZero(); +error ExceedMaxMint(); +error MissingRole(); +error TokenNotExist(); +error TokenMintPaused(); +error DuplicateID(); + contract ItemBoundV1Test is StdCheats, Test { using Strings for uint256; @@ -147,7 +156,9 @@ contract ItemBoundV1Test is StdCheats, Test { LibItems.TokenCreate memory _token = LibItems.TokenCreate({ tokenId: _tokenId, - tokenUri: string(abi.encodePacked("https://something.com", "/", _tokenId.toString())) + tokenUri: string(abi.encodePacked("https://something.com", "/", _tokenId.toString())), + receiver: address(0), + feeBasisPoints: 0 }); _tokens.push(_token); @@ -177,15 +188,17 @@ contract ItemBoundV1Test is StdCheats, Test { function testTokenExists() public { uint256 _tokenId = generateRandomItemId(); - vm.expectRevert("TokenNotExist"); + vm.expectRevert(TokenNotExist.selector); itemBoundProxy.isTokenExist(_tokenId); - vm.expectRevert("TokenNotExist"); + vm.expectRevert(TokenNotExist.selector); itemBoundProxy.adminMintId(playerWallet.addr, _tokenId, 1, true); LibItems.TokenCreate memory _token = LibItems.TokenCreate({ tokenId: _tokenId, - tokenUri: string(abi.encodePacked("https://something222.com", "/", _tokenId.toString())) + tokenUri: string(abi.encodePacked("https://something222.com", "/", _tokenId.toString())), + receiver: address(0), + feeBasisPoints: 0 }); itemBoundProxy.addNewToken(_token); @@ -204,7 +217,9 @@ contract ItemBoundV1Test is StdCheats, Test { LibItems.TokenCreate memory _token = LibItems.TokenCreate({ tokenId: _tokenId, - tokenUri: string(abi.encodePacked("https://something.com", "/", _tokenId.toString())) + tokenUri: string(abi.encodePacked("https://something.com", "/", _tokenId.toString())), + receiver: address(0), + feeBasisPoints: 0 }); _tokens[i] = _token; @@ -230,13 +245,13 @@ contract ItemBoundV1Test is StdCheats, Test { itemBoundProxy.updateTokenMintPaused(_tokenId, true); - vm.expectRevert("TokenMintPaused"); + vm.expectRevert(TokenMintPaused.selector); itemBoundProxy.adminMintId(address(mockERC1155Receiver), _tokenId, 1, true); - vm.expectRevert("TokenMintPaused"); + vm.expectRevert(TokenMintPaused.selector); itemBoundProxy.adminMint(address(mockERC1155Receiver), encodedItems1, true); - vm.expectRevert("TokenMintPaused"); + vm.expectRevert(TokenMintPaused.selector); vm.prank(playerWallet.addr); itemBoundProxy.mint(encodedItems1, 1, true, nonce, signature); @@ -289,7 +304,7 @@ contract ItemBoundV1Test is StdCheats, Test { } function testMintMoreThanLimit() public { - vm.expectRevert("ExceedMaxMint"); + vm.expectRevert(ExceedMaxMint.selector); vm.prank(playerWallet.addr); itemBoundProxy.mint(encodedItems1, 2, true, nonce, signature); } @@ -303,7 +318,7 @@ contract ItemBoundV1Test is StdCheats, Test { (uint256 _nonce, bytes memory _signature) = generateSignature(playerWallet.addr, encodedItems3, minterLabel); - vm.expectRevert("TokenNotExist"); + vm.expectRevert(TokenNotExist.selector); vm.prank(playerWallet.addr); itemBoundProxy.mint(encodedItems3, 1, true, _nonce, _signature); } @@ -507,7 +522,7 @@ contract ItemBoundV1Test is StdCheats, Test { _amount3[0] = 1; _amount3[1] = 1; - vm.expectRevert("ERC1155: duplicate ID"); + vm.expectRevert(DuplicateID.selector); vm.prank(playerWallet.addr); itemBoundProxy.safeBatchTransferFrom(playerWallet.addr, minterWallet.addr, _itemIds3, _amount3, ""); @@ -519,7 +534,7 @@ contract ItemBoundV1Test is StdCheats, Test { } function testTokenURIIfTokenIdNotExist() public { - vm.expectRevert("TokenNotExist"); + vm.expectRevert(TokenNotExist.selector); itemBoundProxy.uri(1); } @@ -528,7 +543,12 @@ contract ItemBoundV1Test is StdCheats, Test { uint256 _level = generateRandomLevel(); // level 1-10 TestLibItems.Tier _tier = generateRandomTier(); // tier 0-4 - LibItems.TokenCreate memory _token = LibItems.TokenCreate({ tokenId: _tokenId, tokenUri: "" }); + LibItems.TokenCreate memory _token = LibItems.TokenCreate({ + tokenId: _tokenId, + tokenUri: "", + receiver: address(0), + feeBasisPoints: 0 + }); itemBoundProxy.addNewToken(_token); @@ -542,7 +562,9 @@ contract ItemBoundV1Test is StdCheats, Test { LibItems.TokenCreate memory _token = LibItems.TokenCreate({ tokenId: _tokenId, - tokenUri: "ipfs://specific-token-uri.com" + tokenUri: "ipfs://specific-token-uri.com", + receiver: address(0), + feeBasisPoints: 0 }); itemBoundProxy.addNewToken(_token); @@ -565,7 +587,12 @@ contract ItemBoundV1Test is StdCheats, Test { uint256 _level = generateRandomLevel(); // level 1-10 TestLibItems.Tier _tier = generateRandomTier(); // tier 0-4 - LibItems.TokenCreate memory _token = LibItems.TokenCreate({ tokenId: _tokenId, tokenUri: "" }); + LibItems.TokenCreate memory _token = LibItems.TokenCreate({ + tokenId: _tokenId, + tokenUri: "", + receiver: address(0), + feeBasisPoints: 0 + }); itemBoundProxy.addNewToken(_token); @@ -594,7 +621,12 @@ contract ItemBoundV1Test is StdCheats, Test { uint256 _level = generateRandomLevel(); // level 1-10 TestLibItems.Tier _tier = generateRandomTier(); // tier 0-4 - LibItems.TokenCreate memory _token = LibItems.TokenCreate({ tokenId: _tokenId, tokenUri: "" }); + LibItems.TokenCreate memory _token = LibItems.TokenCreate({ + tokenId: _tokenId, + tokenUri: "", + receiver: address(0), + feeBasisPoints: 0 + }); itemBoundProxy.addNewToken(_token); @@ -730,14 +762,14 @@ contract ItemBoundV1Test is StdCheats, Test { assertEq(itemBoundProxy.uri(_tokenIds[23]), "https://something-new.com/232"); vm.prank(playerWallet.addr); - LibItems.TokenReturn[] memory allTokensInfo = itemBoundProxy.getAllItems(); + LibItems.TokenReturn[] memory allTokensInfo = itemBoundProxy.getAllItems(playerWallet.addr); assertEq(allTokensInfo.length, 1300); vm.prank(playerWallet.addr); itemBoundProxy.safeTransferFrom(playerWallet.addr, minterWallet.addr, _tokenIds[24], 1, ""); vm.prank(playerWallet.addr); - LibItems.TokenReturn[] memory allTokensInfo2 = itemBoundProxy.getAllItems(); + LibItems.TokenReturn[] memory allTokensInfo2 = itemBoundProxy.getAllItems(playerWallet.addr); assertEq(allTokensInfo2.length, 1299); for (uint256 i = 0; i < allTokensInfo.length; i++) { @@ -756,7 +788,7 @@ contract ItemBoundV1Test is StdCheats, Test { } vm.prank(minterWallet.addr); - LibItems.TokenReturn[] memory allTokensInfo3 = itemBoundProxy.getAllItems(); + LibItems.TokenReturn[] memory allTokensInfo3 = itemBoundProxy.getAllItems(minterWallet.addr); assertEq(allTokensInfo3.length, 1); } @@ -768,13 +800,13 @@ contract ItemBoundV1Test is StdCheats, Test { itemBoundProxy.updateTokenUri(_tokenIds[23], newTokenUri); assertEq(itemBoundProxy.uri(_tokenIds[23]), "https://something-new.com/232"); - LibItems.TokenReturn[] memory allTokensInfo = itemBoundProxy.getAllItemsAdmin(playerWallet.addr); + LibItems.TokenReturn[] memory allTokensInfo = itemBoundProxy.getAllItems(playerWallet.addr); assertEq(allTokensInfo.length, 1300); vm.prank(playerWallet.addr); itemBoundProxy.safeTransferFrom(playerWallet.addr, minterWallet.addr, _tokenIds[24], 1, ""); - LibItems.TokenReturn[] memory allTokensInfo2 = itemBoundProxy.getAllItemsAdmin(playerWallet.addr); + LibItems.TokenReturn[] memory allTokensInfo2 = itemBoundProxy.getAllItems(playerWallet.addr); assertEq(allTokensInfo2.length, 1300); for (uint256 i = 0; i < allTokensInfo.length; i++) { @@ -792,7 +824,7 @@ contract ItemBoundV1Test is StdCheats, Test { } } - LibItems.TokenReturn[] memory allTokensInfo3 = itemBoundProxy.getAllItemsAdmin(minterWallet.addr); + LibItems.TokenReturn[] memory allTokensInfo3 = itemBoundProxy.getAllItems(minterWallet.addr); assertEq(allTokensInfo3.length, 1300); } }