diff --git a/constants/constructor-args.ts b/constants/constructor-args.ts index cf57c116..9d8b754d 100644 --- a/constants/constructor-args.ts +++ b/constants/constructor-args.ts @@ -258,10 +258,24 @@ export const ERC20ChainlinkPaymasterArgs = { export const GameSummaryArgs = { MAINNET: { - _uri: 'FILL_ME', + _name: 'GameSummary', + _symbol: 'GS', + _defaultTokenURI: 'FILL_ME', + _contractURI: 'FILL_ME', + _compoundURI: 'https://api.achievo.xyz/v1/uri/achievements', + _maxPerMint: 1, + _isPaused: false, + _devWallet: 'DEPLOYER_WALLET', }, TESTNET: { - _uri: 'FILL_ME', + _name: 'GameSummary', + _symbol: 'GS', + _defaultTokenURI: 'FILL_ME', + _contractURI: 'FILL_ME', + _compoundURI: 'https://staging-api.achievo.xyz/v1/uri/achievements', + _maxPerMint: 1, + _isPaused: false, + _devWallet: 'DEPLOYER_WALLET', }, }; diff --git a/constants/deployments/deployments-mantle-sepolia.ts b/constants/deployments/deployments-mantle-sepolia.ts deleted file mode 100644 index 8dd31633..00000000 --- a/constants/deployments/deployments-mantle-sepolia.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { GameSummaryArgs } from '@constants/constructor-args'; -import { CONTRACT_FILE_NAME, CONTRACT_NAME, CONTRACT_TYPE } from '@constants/contract'; -import { TENANT } from '@constants/tenant'; - -import { DeploymentContract } from '../../types/deployment-type'; -import { NETWORK_TYPE, NetworkName } from '../network'; - -const chain = NetworkName.MantleSepolia; -const networkType = NETWORK_TYPE.TESTNET; - -export const MANTLE_SEPOLIA_CONTRACTS: DeploymentContract[] = [ - { - contractFileName: CONTRACT_FILE_NAME.GameSummary, - type: CONTRACT_TYPE.GameSummary, - name: CONTRACT_NAME.GameSummary, - chain, - networkType, - tenants: [TENANT.HyperPlay], - verify: true, - upgradable: false, - dependencies: [], - functionCalls: [], - args: GameSummaryArgs.TESTNET, - }, -]; diff --git a/constants/deployments/index.ts b/constants/deployments/index.ts index bf3e63e5..24f105f7 100644 --- a/constants/deployments/index.ts +++ b/constants/deployments/index.ts @@ -1,15 +1,14 @@ import { ARBITRUM_ONE_CONTRACTS } from '@constants/deployments/deployments-arbitrum-one'; import { ARBITRUM_SEPOLIA_CONTRACTS } from '@constants/deployments/deployments-arbitrum-sepolia'; +import { BASE_SEPOLIA_CONTRACTS } from '@constants/deployments/deployments-base-sepolia'; +import { G7_SEPOLIA_ARB_CONTRACTS } from '@constants/deployments/deployments-g7-sepolia-arb'; +import { G7_SEPOLIA_BASE_CONTRACTS } from '@constants/deployments/deployments-g7-sepolia-base'; -import { MANTLE_SEPOLIA_CONTRACTS } from './deployments-mantle-sepolia'; import { POLYGON_MAINNET_CONTRACTS } from './deployments-polygon-mainnet'; import { POLYGON_MUMBAI_CONTRACTS } from './deployments-polygon-mumbai'; import { SEPOLIA_CONTRACTS } from './deployments-sepolia'; import { ZKSYNC_MAINNET_CONTRACTS } from './deployments-zksync-mainnet'; import { ZKSYNC_SEPOLIA_CONTRACTS } from './deployments-zksync-sepolia'; -import { BASE_SEPOLIA_CONTRACTS } from '@constants/deployments/deployments-base-sepolia'; -import { G7_SEPOLIA_BASE_CONTRACTS } from '@constants/deployments/deployments-g7-sepolia-base'; -import { G7_SEPOLIA_ARB_CONTRACTS } from '@constants/deployments/deployments-g7-sepolia-arb'; export const CONTRACTS = [ ...G7_SEPOLIA_ARB_CONTRACTS, @@ -22,7 +21,6 @@ export const CONTRACTS = [ ...ZKSYNC_MAINNET_CONTRACTS, ...ZKSYNC_SEPOLIA_CONTRACTS, ...SEPOLIA_CONTRACTS, - ...MANTLE_SEPOLIA_CONTRACTS, ]; export const ACHIEVO_TMP_DIR = '.achievo'; diff --git a/constants/nonce-deployments/deployments-mantle-mainnet.ts b/constants/nonce-deployments/deployments-mantle-mainnet.ts index 7293adc7..5505820e 100644 --- a/constants/nonce-deployments/deployments-mantle-mainnet.ts +++ b/constants/nonce-deployments/deployments-mantle-mainnet.ts @@ -1,4 +1,4 @@ -import { HelloWorldArgs, LootDropArgs, RewardTokenArgs } from '@constants/constructor-args'; +import { GameSummaryArgs, LootDropArgs, RewardTokenArgs } from '@constants/constructor-args'; import { CONTRACT_FILE_NAME, CONTRACT_NAME, CONTRACT_TYPE } from '@constants/contract'; import { TENANT } from '@constants/tenant'; @@ -35,4 +35,17 @@ export const MANTLE_MAINNET_CONTRACTS: DeploymentContract[] = [ functionCalls: [], args: RewardTokenArgs.MAINNET, }, + { + contractFileName: CONTRACT_FILE_NAME.GameSummary, + type: CONTRACT_TYPE.GameSummary, + name: CONTRACT_NAME.GameSummary, + chain, + networkType, + tenants: [TENANT.HyperPlay], + verify: true, + upgradable: false, + dependencies: [], + functionCalls: [], + args: GameSummaryArgs.MAINNET, + }, ]; diff --git a/constants/nonce-deployments/deployments-mantle-sepolia.ts b/constants/nonce-deployments/deployments-mantle-sepolia.ts index 58931d9c..c0a82aaf 100644 --- a/constants/nonce-deployments/deployments-mantle-sepolia.ts +++ b/constants/nonce-deployments/deployments-mantle-sepolia.ts @@ -1,4 +1,4 @@ -import { HelloWorldArgs, LootDropArgs, RewardTokenArgs } from '@constants/constructor-args'; +import { LootDropArgs, RewardTokenArgs, GameSummaryArgs } from '@constants/constructor-args'; import { CONTRACT_FILE_NAME, CONTRACT_NAME, CONTRACT_TYPE } from '@constants/contract'; import { TENANT } from '@constants/tenant'; @@ -36,16 +36,16 @@ export const MANTLE_SEPOLIA_CONTRACTS: DeploymentContract[] = [ args: RewardTokenArgs.TESTNET, }, { - contractFileName: CONTRACT_FILE_NAME.HelloWorld, - type: CONTRACT_TYPE.HelloWorld, - name: CONTRACT_NAME.HelloWorld, + contractFileName: CONTRACT_FILE_NAME.GameSummary, + type: CONTRACT_TYPE.GameSummary, + name: CONTRACT_NAME.GameSummary, chain, networkType, - tenants: [TENANT.Game7], + tenants: [TENANT.HyperPlay], verify: true, upgradable: false, dependencies: [], functionCalls: [], - args: HelloWorldArgs.TESTNET, + args: GameSummaryArgs.TESTNET, }, ]; diff --git a/contracts/airdrops/FreeMint.sol b/contracts/airdrops/FreeMint.sol index 837b686e..8b8cc642 100644 --- a/contracts/airdrops/FreeMint.sol +++ b/contracts/airdrops/FreeMint.sol @@ -18,8 +18,7 @@ pragma solidity ^0.8.17; // MMMM0cdNMM0cdNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM /** - * Authors: Omar Garcia - * GitHub: https://github.com/ogarciarevett + * Author: Achievo Team - (https://achievo.xyz/) */ import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol"; diff --git a/contracts/bridges/ERC20Bridge.sol b/contracts/bridges/ERC20Bridge.sol index d11f5058..d04b96ab 100644 --- a/contracts/bridges/ERC20Bridge.sol +++ b/contracts/bridges/ERC20Bridge.sol @@ -2,8 +2,7 @@ pragma solidity ^0.8.17; /** - * Author: Omar (https://github.com/ogarciarevett) - * Co-Authors: Max (https://github.com/vasinl124) + * Author: Achievo Team - (https://achievo.xyz/) */ // MMMMNkc. .,oKWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM diff --git a/contracts/ercs/ERCWhitelistSignature.sol b/contracts/ercs/ERCWhitelistSignature.sol index 5381c3c9..39febfa1 100644 --- a/contracts/ercs/ERCWhitelistSignature.sol +++ b/contracts/ercs/ERCWhitelistSignature.sol @@ -2,8 +2,7 @@ pragma solidity ^0.8.17; /** - * Author: Omar (https://github.com/ogarciarevett) - * Co-Authors: Max (https://github.com/vasinl124) + * Author: Achievo Team - (https://achievo.xyz/) */ // MMMMNkc. .,oKWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM diff --git a/contracts/ercs/extensions/Achievo1155Soulbound.sol b/contracts/ercs/extensions/Achievo1155Soulbound.sol index e18ea901..09d20e4a 100644 --- a/contracts/ercs/extensions/Achievo1155Soulbound.sol +++ b/contracts/ercs/extensions/Achievo1155Soulbound.sol @@ -2,8 +2,7 @@ pragma solidity ^0.8.17; /* - * Author: Omar ogarciarevett(https://github.com/ogarciarevett) - * Co-Authors: Max vasinl124(https://github.com/vasinl124) + * Author: Achievo Team - (https://achievo.xyz/) */ // MMMMNkc. .,oKWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM diff --git a/contracts/ercs/extensions/Achievo721Soulbound.sol b/contracts/ercs/extensions/Achievo721Soulbound.sol index a9ae6d5d..645f5ea0 100644 --- a/contracts/ercs/extensions/Achievo721Soulbound.sol +++ b/contracts/ercs/extensions/Achievo721Soulbound.sol @@ -17,8 +17,7 @@ pragma solidity ^0.8.17; // MMMM0cdNMM0cdNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM /* - * Author: Omar (https://github.com/ogarciarevett) - * Co-Authors: Max (https://github.com/vasinl124) + * Author: Achievo Team - (https://achievo.xyz/) */ contract Achievo721Soulbound { diff --git a/contracts/games/AvatarBound.sol b/contracts/games/AvatarBound.sol index 4678f801..d9487a50 100644 --- a/contracts/games/AvatarBound.sol +++ b/contracts/games/AvatarBound.sol @@ -2,8 +2,7 @@ pragma solidity ^0.8.17; /** - * Author: Omar (https://github.com/ogarciarevett) - * Co-Authors: Max (https://github.com/vasinl124) + * Author: Achievo Team - (https://achievo.xyz/) */ // MMMMNkc. .,oKWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM diff --git a/contracts/games/GameSummary.sol b/contracts/games/GameSummary.sol index a28aac72..1ddfcca0 100644 --- a/contracts/games/GameSummary.sol +++ b/contracts/games/GameSummary.sol @@ -1,6 +1,10 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.17; +/** + * Author: Achievo Team - (https://achievo.xyz/) + */ + // MMMMNkc. .,oKWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM // MWXd,. .cONMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM // Wx' .cKMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM @@ -16,315 +20,360 @@ pragma solidity ^0.8.17; // MMNx'.dWMMK;.:0WMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM // MMMM0cdNMM0cdNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM -/** - * Authors: Omar Garcia - * GitHub: https://github.com/ogarciarevett - */ - -import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; -import "@openzeppelin/contracts/access/AccessControl.sol"; -import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import "@openzeppelin/contracts/security/Pausable.sol"; -import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; -import "@openzeppelin/contracts/utils/Strings.sol"; +import { ERC1155 } from "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; +import { ERC1155Burnable } from "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Burnable.sol"; +import { ERC1155Supply } from "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Supply.sol"; +import { AccessControl } from "@openzeppelin/contracts/access/AccessControl.sol"; +import { Pausable } from "@openzeppelin/contracts/security/Pausable.sol"; +import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; +import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; +import { ReentrancyGuard } from "@openzeppelin/contracts/security/ReentrancyGuard.sol"; +import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; + +import { Achievo1155Soulbound } from "../ercs/extensions/Achievo1155Soulbound.sol"; +import { ERCWhitelistSignature } from "../ercs/ERCWhitelistSignature.sol"; +import { LibGameSummary } from "../libraries/LibGameSummary.sol"; + +error AddressIsZero(); +error InvalidInput(); + +contract GameSummary is + ERC1155Burnable, + ERC1155Supply, + Achievo1155Soulbound, + ERCWhitelistSignature, + AccessControl, + Pausable, + ReentrancyGuard, + Initializable +{ + event BaseURIChanged(string indexed uri); + event ContractURIChanged(string indexed uri); + event CompoundURIChanged(string indexed uri); + event CompoundURIEnabledChanged(bool enabled); -// This contract contains only the phase 1 of the GameSummaries contract -contract GameSummary is ERC1155, AccessControl, Pausable, ReentrancyGuard { bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); - bytes32 public constant GAME_CREATOR_ROLE = keccak256("GAME_CREATOR_ROLE"); + bytes32 public constant MANAGER_ROLE = keccak256("MANAGER_ROLE"); + bytes32 public constant DEV_CONFIG_ROLE = keccak256("DEV_CONFIG_ROLE"); - event GameSummaryUpdated(address indexed indexer, uint256 indexed tokenId); - event GameSummaryMinted(address indexed player, uint256 indexed gameId, uint256 totalAchievements); - event PlayerGameSummaryMinted(address indexed player, uint256 indexed gameId, uint256 achievements); - event PlayerGameSummaryUpdated(address indexed player, uint256 indexed gameId, uint256 achievements); - event SignerAdded(address signer); - event SignerRemoved(address signer); - event GameSummaryMintedPaused(bool paused); + string public contractURI; + string private baseURI; + string public compoundURI; - string public baseUri; + bool public compoundURIEnabled; + bool public isOneTokenPerWallet = true; - struct GameSummary { - uint256 tokenId; - uint256 storeId; - uint256 gameId; - string name; - string image; - string externalURI; - uint256 totalAchievements; - } + string public name; + string public symbol; + using Strings for uint256; - struct PlayerGameData { - uint256 tokenId; - uint256 achievementsMinted; - bool soulbounded; - } + uint256 public MAX_PER_MINT; + + mapping(uint256 => bool) private tokenExists; - // tokenId(storeId+0+gameId) => common game data - mapping(uint256 => GameSummary) public commonGameSummaries; + mapping(uint256 => uint256) public storeIds; // tokenId => storeIds + mapping(uint256 => uint256) public playerIds; // tokenId => playerIds + mapping(uint256 => uint256) public gameIds; // tokenId => gameIds - // player address => tokenId(storeId+0+gameId) => player game data - mapping(address => mapping(uint256 => PlayerGameData)) public playerGameData; + mapping(uint256 => bool) public isTokenMintPaused; // tokenId => bool - default is false - mapping(address => bool) public whitelistSigners; + uint256[] public itemIds; - // bytes(signature) => used - mapping(bytes => bool) public usedSignatures; + mapping(address => mapping(uint256 => bool)) private tokenIdProcessed; - modifier onlyOnceSignature(bytes memory signature) { - require(usedSignatures[signature] != true, "Signature and nonce already used"); + 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); + + modifier maxPerMintCheck(uint256 amount) { + if (amount > MAX_PER_MINT) { + revert("ExceedMaxMint"); + } _; } - constructor(string memory _uri) ERC1155(_uri) { - baseUri = _uri; - _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); - } + constructor(address devWallet) ERC1155("") { + if (devWallet == address(0)) { + revert AddressIsZero(); + } + _grantRole(DEFAULT_ADMIN_ROLE, devWallet); + } + + function initialize( + string memory _name, + string memory _symbol, + string memory _initBaseURI, + string memory _contractURI, + string memory _compoundURI, + uint256 _maxPerMint, + bool _isPaused, + address devWallet + ) external initializer onlyRole(DEFAULT_ADMIN_ROLE) { + if (devWallet == address(0)) { + revert AddressIsZero(); + } - function pause() public onlyRole(DEFAULT_ADMIN_ROLE) { - _pause(); - } + _grantRole(DEFAULT_ADMIN_ROLE, devWallet); + _grantRole(DEV_CONFIG_ROLE, devWallet); + _grantRole(MINTER_ROLE, devWallet); + _grantRole(MANAGER_ROLE, devWallet); + _addWhitelistSigner(devWallet); - function unpause() public onlyRole(DEFAULT_ADMIN_ROLE) { - _unpause(); - } + name = _name; + symbol = _symbol; + baseURI = _initBaseURI; + contractURI = _contractURI; + MAX_PER_MINT = _maxPerMint; - function getTokenId(uint256 storeId, uint256 gameId) public pure returns (uint256) { - uint256 tokenId = uint256(keccak256(abi.encode(storeId, gameId))); - return tokenId; - } + compoundURIEnabled = true; + compoundURI = _compoundURI; - function setBaseUri(string memory _uri) public onlyRole(DEFAULT_ADMIN_ROLE) { - baseUri = _uri; + if (_isPaused) _pause(); } - function setSigner(address _signer) public onlyRole(DEFAULT_ADMIN_ROLE) { - whitelistSigners[_signer] = true; - emit SignerAdded(_signer); - } + function getAllItems() public view returns (LibGameSummary.GameSummaryReturn[] memory) { + uint256 totalTokens = itemIds.length; + LibGameSummary.GameSummaryReturn[] memory GameSummaryReturns = new LibGameSummary.GameSummaryReturn[]( + totalTokens + ); - function removeSigner(address signer) public onlyRole(DEFAULT_ADMIN_ROLE) { - whitelistSigners[signer] = false; - emit SignerRemoved(signer); - } + uint index; + for (uint i = 0; i < totalTokens; i++) { + uint256 tokenId = itemIds[i]; + uint256 amount = balanceOf(_msgSender(), tokenId); + + if (amount > 0) { + LibGameSummary.GameSummaryReturn memory GameSummaryReturn = LibGameSummary.GameSummaryReturn({ + tokenId: tokenId, + tokenUri: uri(tokenId), + storeId: storeIds[tokenId], + playerId: playerIds[tokenId], + gameId: gameIds[tokenId], + amount: amount + }); + GameSummaryReturns[index] = GameSummaryReturn; + index++; + } + } - function recoverAddress(uint256 nonce, bytes memory signature) private view returns (address) { - bytes32 message = keccak256(abi.encodePacked(msg.sender, nonce)); - bytes32 hash = ECDSA.toEthSignedMessageHash(message); - address signer = ECDSA.recover(hash, signature); - return signer; + // truncate the array + LibGameSummary.GameSummaryReturn[] memory returnsTruncated = new LibGameSummary.GameSummaryReturn[](index); + for (uint i = 0; i < index; i++) { + returnsTruncated[i] = GameSummaryReturns[i]; + } + + return returnsTruncated; } - function verifySignature(uint256 nonce, bytes memory signature) private returns (bool) { - address signer = recoverAddress(nonce, signature); - if (whitelistSigners[signer]) { - usedSignatures[signature] = true; - return true; - } else { - return false; + function getAllItemsAdmin( + address _owner + ) public view onlyRole(MINTER_ROLE) returns (LibGameSummary.GameSummaryReturn[] memory) { + uint256 totalTokens = itemIds.length; + LibGameSummary.GameSummaryReturn[] memory GameSummaryReturns = new LibGameSummary.GameSummaryReturn[]( + totalTokens + ); + + uint index; + for (uint i = 0; i < totalTokens; i++) { + uint256 tokenId = itemIds[i]; + uint256 amount = balanceOf(_owner, tokenId); + + LibGameSummary.GameSummaryReturn memory GameSummaryReturn = LibGameSummary.GameSummaryReturn({ + tokenId: tokenId, + tokenUri: uri(tokenId), + storeId: storeIds[tokenId], + playerId: playerIds[tokenId], + gameId: gameIds[tokenId], + amount: amount + }); + GameSummaryReturns[index] = GameSummaryReturn; + index++; } - } - function getGameSummary(uint256 tokenId) public view returns (GameSummary memory) { - return commonGameSummaries[tokenId]; + // truncate the array + LibGameSummary.GameSummaryReturn[] memory returnsTruncated = new LibGameSummary.GameSummaryReturn[](index); + for (uint i = 0; i < index; i++) { + returnsTruncated[i] = GameSummaryReturns[i]; + } + + return returnsTruncated; } - function getGameSummaries(uint256[] calldata tokenIds) public view returns (GameSummary[] memory) { - GameSummary[] memory summaries = new GameSummary[](tokenIds.length); - for (uint i = 0; i < tokenIds.length; i++) { - summaries[i] = commonGameSummaries[tokenIds[i]]; + function isTokenExist(uint256 _tokenId) public view returns (bool) { + if (!tokenExists[_tokenId]) { + return false; + } + return true; + } + + function _verifyContractChainIdAndDecode( + bytes calldata data + ) private view returns (uint256[] memory, uint256[] memory, uint256[] memory) { + uint256 currentChainId = getChainID(); + ( + address contractAddress, + uint256 chainId, + uint256[] memory _storeIds, + uint256[] memory _playerIds, + uint256[] memory _gameIds + ) = _decodeData(data); + + if (chainId != currentChainId || contractAddress != address(this)) { + revert InvalidInput(); } - return summaries; + return (_storeIds, _playerIds, _gameIds); + } + + function decodeData( + bytes calldata _data + ) + public + view + onlyRole(DEV_CONFIG_ROLE) + returns (address, uint256, uint256[] memory, uint256[] memory, uint256[] memory) + { + return _decodeData(_data); + } + + function _decodeData( + bytes calldata _data + ) private pure returns (address, uint256, uint256[] memory, uint256[] memory, uint256[] memory) { + ( + address contractAddress, + uint256 chainId, + uint256[] memory _storeIds, + uint256[] memory _playerIds, + uint256[] memory _gameIds + ) = abi.decode(_data, (address, uint256, uint256[], uint256[], uint256[])); + return (contractAddress, chainId, _storeIds, _playerIds, _gameIds); + } + + function pause() external onlyRole(MANAGER_ROLE) { + _pause(); } - function getPlayerGameData(address player, uint256 tokenId) public view returns (PlayerGameData memory) { - return playerGameData[player][tokenId]; + function unpause() external onlyRole(MANAGER_ROLE) { + _unpause(); } - function getPlayerGamesData( - address player, - uint256[] calldata tokenIds - ) public view returns (PlayerGameData[] memory) { - PlayerGameData[] memory playerGamesData = new PlayerGameData[](tokenIds.length); - for (uint i = 0; i < tokenIds.length; i++) { - playerGamesData[i] = playerGameData[player][tokenIds[i]]; - } - return playerGamesData; + function getTokenId(uint256 storeId, uint256 playerId, uint256 gameId) public pure returns (uint256) { + uint256 tokenId = uint256(keccak256(abi.encode(storeId, playerId, gameId))); + return tokenId; } - function updateCommonGameSummary( - uint256 tokenId, - string memory newName, - string memory newImageURI, - string memory newExternalURI, - uint256 newTotalAchievements - ) public onlyRole(GAME_CREATOR_ROLE) whenNotPaused { - require(tokenId > 0, "TokenId must be greater than 0"); - require(commonGameSummaries[tokenId].storeId != 0, "Token doesn't exists"); - GameSummary storage gameData = commonGameSummaries[tokenId]; - gameData.name = newName; - gameData.image = newImageURI; - gameData.externalURI = newExternalURI; - gameData.totalAchievements = newTotalAchievements; - emit GameSummaryUpdated(msg.sender, tokenId); - } - - function addPlayerAchievements(address player, uint256 tokenId, uint256 newAchievements) private { - require(tokenId > 0, "TokenId must be greater than 0"); - require(playerGameData[player][tokenId].tokenId != 0, "Token doesn't exists"); - PlayerGameData storage playerData = playerGameData[player][tokenId]; - if (playerData.achievementsMinted + newAchievements > commonGameSummaries[tokenId].totalAchievements) { - revert("total achievements exceeded"); - } - playerData.achievementsMinted += newAchievements; - emit PlayerGameSummaryUpdated(player, tokenId, playerData.achievementsMinted); - } + function _addNewToken(LibGameSummary.GameSummaryCreate memory _token) internal { + tokenExists[_token.tokenId] = true; - function adminUpdatePlayerAchievements( - address player, - uint256 tokenId, - uint256 newAchievements - ) public onlyRole(DEFAULT_ADMIN_ROLE) whenNotPaused { - addPlayerAchievements(player, tokenId, newAchievements); + storeIds[_token.tokenId] = _token.storeId; + playerIds[_token.tokenId] = _token.playerId; + gameIds[_token.tokenId] = _token.gameId; + + itemIds.push(_token.tokenId); + emit TokenAdded(_token.tokenId); } - function updatePlayerAchievementsWithSignature( - uint256 tokenId, - uint256 newAchievements, - uint256 nonce, - bytes memory signature - ) public nonReentrant onlyOnceSignature(signature) whenNotPaused { - require(verifySignature(nonce, signature), "Invalid signature"); - addPlayerAchievements(msg.sender, tokenId, newAchievements); - } - - function createCommonGameSummary( - uint256 storeId, - uint256 gameId, - string memory name, - string memory onChainURI, - string memory externalURI, - uint256 totalAchievements - ) public onlyRole(GAME_CREATOR_ROLE) { - require(gameId > 0, "GameId must be greater than 0"); - require(storeId > 0, "StoreId must be greater than 0"); - uint256 tokenId = getTokenId(storeId, gameId); - require(commonGameSummaries[tokenId].tokenId != tokenId, "CommonGameSummary already exists"); - commonGameSummaries[tokenId] = GameSummary( - tokenId, - storeId, - gameId, - name, - onChainURI, - externalURI, - totalAchievements - ); - emit GameSummaryMinted(msg.sender, tokenId, totalAchievements); + function updateTokenMintPaused(uint256 _tokenId, bool _isTokenMintPaused) public onlyRole(MANAGER_ROLE) { + isTokenMintPaused[_tokenId] = _isTokenMintPaused; } - function mintGameSummary( - address player, - uint256 gameId, - uint256 achievementsLength, - uint256 storeId, + function _mintBatch( + address to, + uint256[] memory _storeIds, + uint256[] memory _playerIds, + uint256[] memory _gameIds, + uint256 amount, bool soulbound ) private { - require(storeId > 0, "StoreId must be greater than 0"); - require(gameId > 0, "GameId must be greater than 0"); - uint256 tokenId = getTokenId(storeId, gameId); - require(commonGameSummaries[tokenId].tokenId == tokenId, "This game is not allowed yet"); - require(playerGameData[player][tokenId].tokenId == 0, "Token already exists"); - _mint(player, tokenId, 1, ""); - playerGameData[player][tokenId] = PlayerGameData(tokenId, achievementsLength, soulbound); - emit PlayerGameSummaryMinted(player, tokenId, achievementsLength); + uint256[] memory _tokenIds = new uint256[](_storeIds.length); + for (uint256 i = 0; i < _storeIds.length; i++) { + uint256 _id = _internalMint(to, _storeIds[i], _playerIds[i], _gameIds[i], amount, soulbound); + _tokenIds[i] = _id; + } + emit Minted(to, _tokenIds, amount, soulbound); } - function adminMintGameSummary( + function _internalMint( address to, - uint256 gameId, - uint256 achievementsLength, - uint256 storeId, + uint256 _storeId, + uint256 _playerId, + uint256 _gameId, + uint256 amount, bool soulbound - ) public onlyRole(MINTER_ROLE) whenNotPaused { - mintGameSummary(to, gameId, achievementsLength, storeId, soulbound); - } + ) private returns (uint256) { + uint256 _id = getTokenId(_storeId, _playerId, _gameId); - function mintGameSummaryWithSignature( - uint256 gameId, - uint256 achievementsLength, - uint256 storeId, - uint256 nonce, - bytes memory signature - ) public nonReentrant onlyOnceSignature(signature) whenNotPaused { - require(verifySignature(nonce, signature), "Invalid signature"); - mintGameSummary(msg.sender, gameId, achievementsLength, storeId, true); - } - - function adminBatchPlayerUpdateAchievements( - address[] memory players, - uint256[] calldata tokenIds, - uint256[] calldata newAchievements - ) public onlyRole(DEFAULT_ADMIN_ROLE) whenNotPaused { - require(players.length == tokenIds.length, "The players and tokenIds arrays must have the same length"); - require( - players.length == newAchievements.length, - "The players and newAchievements arrays must have the same length" - ); - for (uint i = 0; i < players.length; i++) { - addPlayerAchievements(players[i], tokenIds[i], newAchievements[i]); + if (isOneTokenPerWallet && balanceOf(to, _id) > 0) { + revert("AlreadyMinted"); } - } - function batchPlayerUpdateAchievementsWithSignature( - uint256[] calldata tokenIds, - uint256[] calldata newAchievements, - uint256 nonce, - bytes memory signature - ) public nonReentrant onlyOnceSignature(signature) whenNotPaused { - require(verifySignature(nonce, signature), "Invalid signature"); - require( - tokenIds.length == newAchievements.length, - "The players and newAchievements arrays must have the same length" - ); - for (uint i = 0; i < tokenIds.length; i++) { - addPlayerAchievements(msg.sender, tokenIds[i], newAchievements[i]); + if (isTokenMintPaused[_id]) { + revert("TokenMintPaused"); } - } - function adminBatchMintGameSummary( - address[] calldata players, - uint256[] calldata gameIds, - uint256[] calldata achievementsLength, - uint256[] calldata storeIds, - bool[] calldata soulbounds - ) public onlyRole(MINTER_ROLE) whenNotPaused { - require(players.length == gameIds.length, "The players and gameIds arrays must have the same length"); - require(players.length == storeIds.length, "The players and storeIds arrays must have the same length"); - require( - players.length == achievementsLength.length, - "The players and newAchievements arrays must have the same length" - ); - for (uint i = 0; i < players.length; i++) { - mintGameSummary(players[i], gameIds[i], achievementsLength[i], storeIds[i], soulbounds[i]); + if (!isTokenExist(_id)) { + LibGameSummary.GameSummaryCreate memory gameSummaryCreate = LibGameSummary.GameSummaryCreate({ + tokenId: _id, + tokenUri: "", + storeId: _storeId, + playerId: _playerId, + gameId: _gameId + }); + _addNewToken(gameSummaryCreate); } + + if (soulbound) { + _soulbound(to, _id, amount); + } + + _mint(to, _id, amount, ""); + return _id; } - function batchMintGameSummaryWithSignature( - uint256[] calldata gameIds, - uint256[] calldata newAchievements, - uint256[] calldata storeIds, + function mint( + bytes calldata data, + uint256 amount, + bool soulbound, uint256 nonce, - bytes memory signature - ) public whenNotPaused onlyOnceSignature(signature) nonReentrant { - require(verifySignature(nonce, signature), "Invalid signature"); - require(gameIds.length == storeIds.length, "The gameIds and storeIds arrays must have the same length"); - require( - gameIds.length == newAchievements.length, - "The gameIds and newAchievements arrays must have the same length" - ); + bytes calldata signature + ) external nonReentrant signatureCheck(_msgSender(), nonce, data, signature) maxPerMintCheck(amount) whenNotPaused { + ( + uint256[] memory _storeIds, + uint256[] memory _playerIds, + uint256[] memory _gameIds + ) = _verifyContractChainIdAndDecode(data); + _mintBatch(_msgSender(), _storeIds, _playerIds, _gameIds, amount, soulbound); + } + + function adminMint(address to, bytes calldata data, bool soulbound) external onlyRole(MINTER_ROLE) whenNotPaused { + ( + uint256[] memory _storeIds, + uint256[] memory _playerIds, + uint256[] memory _gameIds + ) = _verifyContractChainIdAndDecode(data); + _mintBatch(to, _storeIds, _playerIds, _gameIds, 1, soulbound); + } + + function adminMintId( + address to, + uint256 _storeId, + uint256 _playerId, + uint256 _gameId, + uint256 amount, + bool soulbound + ) external onlyRole(MINTER_ROLE) whenNotPaused { + uint256 _id = _internalMint(to, _storeId, _playerId, _gameId, amount, soulbound); + emit MintedId(to, _id, amount, soulbound); + } - for (uint i = 0; i < gameIds.length; i++) { - mintGameSummary(msg.sender, gameIds[i], newAchievements[i], storeIds[i], true); - } + function _beforeTokenTransfer( + address operator, + address from, + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) internal virtual override(ERC1155, ERC1155Supply) { + super._beforeTokenTransfer(operator, from, to, ids, amounts, data); } function safeTransferFrom( @@ -333,16 +382,7 @@ contract GameSummary is ERC1155, AccessControl, Pausable, ReentrancyGuard { uint256 _id, uint256 _amount, bytes memory _data - ) public virtual override { - require(playerGameData[_from][_id].tokenId != 0, "Token doesn't exists"); - require(!playerGameData[_from][_id].soulbounded, "You can't transfer this token"); - PlayerGameData storage playerData = playerGameData[_from][_id]; - uint256 transferachievements = playerData.achievementsMinted; - playerGameData[_from][_id] = PlayerGameData(0, 0, false); - if (playerGameData[_to][_id].tokenId != 0) { - revert("Token already exists, not possible to send it"); - } - playerGameData[_to][_id] = PlayerGameData(_id, transferachievements, false); + ) public virtual override soulboundCheckAndSync(_from, _to, _id, _amount, balanceOf(_from, _id)) { super.safeTransferFrom(_from, _to, _id, _amount, _data); } @@ -352,43 +392,168 @@ contract GameSummary is ERC1155, AccessControl, Pausable, ReentrancyGuard { uint256[] memory _ids, uint256[] memory _amounts, bytes memory _data - ) public virtual override { - for (uint i = 0; i < _ids.length; i++) { - require(playerGameData[_from][_ids[i]].tokenId != 0, "Token doesn't exists"); - require(!playerGameData[_from][_ids[i]].soulbounded, "You can't transfer this token"); + ) + 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); - for (uint i = 0; i < _ids.length; i++) { - PlayerGameData storage playerData = playerGameData[_from][_ids[i]]; - uint256 transferachievements = playerData.achievementsMinted; - playerGameData[_from][_ids[i]] = PlayerGameData(0, 0, false); - playerGameData[_to][_ids[i]] = PlayerGameData(_ids[i], transferachievements, false); + // 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 uri(uint256 _tokenId) public view override returns (string memory) { - return string(abi.encodePacked(baseUri, Strings.toString(_tokenId), ".json")); - } + 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]); + } - function burn(uint256 tokenId) public nonReentrant { - require(playerGameData[msg.sender][tokenId].tokenId != 0, "Token doesn't exists"); - _burn(msg.sender, tokenId, 1); - playerGameData[msg.sender][tokenId] = PlayerGameData(0, 0, false); + return batchBalances; } - function burnBatch(uint256[] memory tokenIds) public nonReentrant { - uint256[] memory amounts = new uint256[](tokenIds.length); - for (uint i = 0; i < tokenIds.length; i++) { - require(playerGameData[msg.sender][tokenIds[i]].tokenId != 0, "Token doesn't exist"); - require(balanceOf(msg.sender, tokenIds[i]) > 0, "You don't have a token to burn"); - amounts[i] = 1; - playerGameData[msg.sender][tokenIds[i]] = PlayerGameData(0, 0, false); + function burn( + address to, + uint256 tokenId, + uint256 amount + ) + public + virtual + override + nonReentrant + soulboundCheckAndSync(to, address(0), tokenId, amount, balanceOf(to, tokenId)) + { + ERC1155Burnable.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; + } + + ERC1155Burnable.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; } - _burnBatch(msg.sender, tokenIds, amounts); } function supportsInterface(bytes4 interfaceId) public view override(ERC1155, AccessControl) returns (bool) { return super.supportsInterface(interfaceId); } + + function uri(uint256 tokenId) public view override returns (string memory) { + if (!isTokenExist(tokenId)) { + revert("TokenNotExist"); + } + + if (compoundURIEnabled) { + // "{compoundURI}/0x1234567890123456789012345678901234567890/{tokenId}"; + return + string( + abi.encodePacked( + compoundURI, + "/", + Strings.toHexString(uint160(address(this)), 20), + "/", + Strings.toString(storeIds[tokenId]), + "-", + Strings.toString(playerIds[tokenId]), + "-", + Strings.toString(gameIds[tokenId]) + ) + ); + } + + return string(abi.encodePacked(baseURI, "/", tokenId.toString())); + } + + function setBaseUri(string memory _uri) public onlyRole(DEV_CONFIG_ROLE) { + baseURI = _uri; + emit BaseURIChanged(baseURI); + } + + function setCompoundURIEnabled(bool _compoundURIEnabled) public onlyRole(DEV_CONFIG_ROLE) { + if (_compoundURIEnabled == compoundURIEnabled) { + revert InvalidInput(); + } + + compoundURIEnabled = _compoundURIEnabled; + emit CompoundURIEnabledChanged(_compoundURIEnabled); + } + + function setCompoundURI(string memory _compoundURI) public onlyRole(DEV_CONFIG_ROLE) { + compoundURI = _compoundURI; + emit CompoundURIChanged(_compoundURI); + } + + function setContractURI(string memory _contractURI) public onlyRole(DEV_CONFIG_ROLE) { + contractURI = _contractURI; + emit ContractURIChanged(_contractURI); + } + + function updateWhitelistAddress(address _address, bool _isWhitelisted) external onlyRole(DEV_CONFIG_ROLE) { + _updateWhitelistAddress(_address, _isWhitelisted); + } + + 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); + } + + function getChainID() public view returns (uint256) { + uint256 id; + assembly { + id := chainid() + } + return id; + } } diff --git a/contracts/games/LevelsBound.sol b/contracts/games/LevelsBound.sol index 9cb16305..caca01c4 100644 --- a/contracts/games/LevelsBound.sol +++ b/contracts/games/LevelsBound.sol @@ -17,8 +17,7 @@ pragma solidity ^0.8.17; // MMMM0cdNMM0cdNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM /** - * Authors: Omar Garcia - * GitHub: https://github.com/ogarciarevett + * Author: Achievo Team - (https://achievo.xyz/) */ import { ERC1155 } from "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; diff --git a/contracts/governance/Whitelist.sol b/contracts/governance/Whitelist.sol index 3a80ae98..5186d789 100644 --- a/contracts/governance/Whitelist.sol +++ b/contracts/governance/Whitelist.sol @@ -2,8 +2,7 @@ pragma solidity ^0.8.17; /** - * Author: Omar (https://github.com/ogarciarevett) - * Co-Authors: Max (https://github.com/vasinl124) + * Author: Achievo Team - (https://achievo.xyz/) */ // MMMMNkc. .,oKWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM diff --git a/contracts/interfaces/IMarketplace.sol b/contracts/interfaces/IMarketplace.sol index fa0e668f..05b91c90 100644 --- a/contracts/interfaces/IMarketplace.sol +++ b/contracts/interfaces/IMarketplace.sol @@ -2,8 +2,7 @@ pragma solidity ^0.8.17; /** - * Author: Omar (https://github.com/ogarciarevett) - * Co-Authors: Max (https://github.com/vasinl124) + * Author: Achievo Team - (https://achievo.xyz/) */ // MMMMNkc. .,oKWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM @@ -21,7 +20,6 @@ pragma solidity ^0.8.17; // MMNx'.dWMMK;.:0WMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM // MMMM0cdNMM0cdNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM - /** * @author Omar * @@ -528,4 +526,4 @@ interface IOffers { /// @notice Returns all valid offers. An offer is valid if the offeror owns and has approved Marketplace to transfer the offer amount of currency. function getAllValidOffers(uint256 _startId, uint256 _endId) external view returns (Offer[] memory offers); -} \ No newline at end of file +} diff --git a/contracts/libraries/LibGameSummary.sol b/contracts/libraries/LibGameSummary.sol new file mode 100644 index 00000000..093520e8 --- /dev/null +++ b/contracts/libraries/LibGameSummary.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +// 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 + +library LibGameSummary { + struct GameSummaryCreate { + uint256 tokenId; + string tokenUri; + uint256 storeId; + uint256 playerId; + uint256 gameId; + } + + struct GameSummaryReturn { + uint256 tokenId; + string tokenUri; + uint256 storeId; + uint256 playerId; + uint256 gameId; + uint256 amount; + } +} diff --git a/contracts/mocks/MockErc20.sol b/contracts/mocks/MockErc20.sol index ac0d5a60..4d68033e 100644 --- a/contracts/mocks/MockErc20.sol +++ b/contracts/mocks/MockErc20.sol @@ -3,8 +3,7 @@ pragma solidity ^0.8.17; /** - * Authors: Omar Garcia - * GitHub: https://github.com/ogarciarevett + * Author: Achievo Team - (https://achievo.xyz/) */ // MMMMNkc. .,oKWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM diff --git a/contracts/soulbounds/AdminERC1155Soulbound.sol b/contracts/soulbounds/AdminERC1155Soulbound.sol index ec99b71d..b51a9d3b 100644 --- a/contracts/soulbounds/AdminERC1155Soulbound.sol +++ b/contracts/soulbounds/AdminERC1155Soulbound.sol @@ -2,8 +2,7 @@ pragma solidity ^0.8.17; /** - * Author: Max vasinl124(https://github.com/vasinl124) - * Co-Authors: Omar ogarciarevett(https://github.com/ogarciarevett) + * Author: Achievo Team - (https://achievo.xyz/) */ // MMMMNkc. .,oKWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM diff --git a/contracts/soulbounds/ERC1155RoyaltiesSoulbound.sol b/contracts/soulbounds/ERC1155RoyaltiesSoulbound.sol index 66f3a000..69977da8 100644 --- a/contracts/soulbounds/ERC1155RoyaltiesSoulbound.sol +++ b/contracts/soulbounds/ERC1155RoyaltiesSoulbound.sol @@ -2,8 +2,7 @@ pragma solidity ^0.8.17; /** - * Author: Max vasinl124(https://github.com/vasinl124) - * Co-Authors: Omar ogarciarevett(https://github.com/ogarciarevett) + * Author: Achievo Team - (https://achievo.xyz/) */ // MMMMNkc. .,oKWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM diff --git a/contracts/soulbounds/ERC1155Soulbound.sol b/contracts/soulbounds/ERC1155Soulbound.sol index 1fac2e2b..a6d34e97 100644 --- a/contracts/soulbounds/ERC1155Soulbound.sol +++ b/contracts/soulbounds/ERC1155Soulbound.sol @@ -2,8 +2,7 @@ pragma solidity ^0.8.17; /** - * Author: Max vasinl124(https://github.com/vasinl124) - * Co-Authors: Omar ogarciarevett(https://github.com/ogarciarevett) + * Author: Achievo Team - (https://achievo.xyz/) */ // MMMMNkc. .,oKWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM @@ -409,7 +408,9 @@ contract ERC1155Soulbound is } } - function supportsInterface(bytes4 interfaceId) public view override(ERC2981, ERC1155, AccessControl) returns (bool) { + function supportsInterface( + bytes4 interfaceId + ) public view override(ERC2981, ERC1155, AccessControl) returns (bool) { return super.supportsInterface(interfaceId); } diff --git a/contracts/soulbounds/ERC721Soulbound.sol b/contracts/soulbounds/ERC721Soulbound.sol index 3a3e5799..e60b120b 100644 --- a/contracts/soulbounds/ERC721Soulbound.sol +++ b/contracts/soulbounds/ERC721Soulbound.sol @@ -2,8 +2,7 @@ pragma solidity ^0.8.17; /** - * Author: Omar (https://github.com/ogarciarevett) - * Co-Authors: Max (https://github.com/vasinl124) + * Author: Achievo Team - (https://achievo.xyz/) */ // MMMMNkc. .,oKWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM diff --git a/contracts/soulbounds/LootDrop.sol b/contracts/soulbounds/LootDrop.sol index 629a7a3b..a6992af5 100644 --- a/contracts/soulbounds/LootDrop.sol +++ b/contracts/soulbounds/LootDrop.sol @@ -2,8 +2,7 @@ pragma solidity ^0.8.17; /** - * Author: Omar (https://github.com/ogarciarevett) - * Co-Authors: Max (https://github.com/vasinl124) + * Author: Achievo Team - (https://achievo.xyz/) */ // MMMMNkc. .,oKWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM diff --git a/contracts/upgradeables/bridges/ERC20BridgeV1.sol b/contracts/upgradeables/bridges/ERC20BridgeV1.sol index 1636bebf..f820ec2c 100644 --- a/contracts/upgradeables/bridges/ERC20BridgeV1.sol +++ b/contracts/upgradeables/bridges/ERC20BridgeV1.sol @@ -2,8 +2,7 @@ pragma solidity ^0.8.17; /** - * Author: Omar (https://github.com/ogarciarevett) - * Co-Authors: Max (https://github.com/vasinl124) + * Author: Achievo Team - (https://achievo.xyz/) */ // MMMMNkc. .,oKWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM diff --git a/contracts/upgradeables/ercs/ERCWhitelistSignatureUpgradeable.sol b/contracts/upgradeables/ercs/ERCWhitelistSignatureUpgradeable.sol index c27e83f5..b3b8e726 100644 --- a/contracts/upgradeables/ercs/ERCWhitelistSignatureUpgradeable.sol +++ b/contracts/upgradeables/ercs/ERCWhitelistSignatureUpgradeable.sol @@ -2,8 +2,7 @@ pragma solidity ^0.8.17; /** - * Author: Omar (https://github.com/ogarciarevett) - * Co-Authors: Max (https://github.com/vasinl124) + * Author: Achievo Team - (https://achievo.xyz/) */ // MMMMNkc. .,oKWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM diff --git a/contracts/upgradeables/ercs/extensions/Achievo1155SoulboundUpgradeable.sol b/contracts/upgradeables/ercs/extensions/Achievo1155SoulboundUpgradeable.sol index fd00161e..a09809ee 100644 --- a/contracts/upgradeables/ercs/extensions/Achievo1155SoulboundUpgradeable.sol +++ b/contracts/upgradeables/ercs/extensions/Achievo1155SoulboundUpgradeable.sol @@ -2,8 +2,7 @@ pragma solidity ^0.8.17; /** - * Author: Omar (https://github.com/ogarciarevett) - * Co-Authors: Max (https://github.com/vasinl124) + * Author: Achievo Team - (https://achievo.xyz/) */ // MMMMNkc. .,oKWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM diff --git a/contracts/upgradeables/ercs/extensions/Achievo721SoulboundUpgradeable.sol b/contracts/upgradeables/ercs/extensions/Achievo721SoulboundUpgradeable.sol index 402ab56c..19478549 100644 --- a/contracts/upgradeables/ercs/extensions/Achievo721SoulboundUpgradeable.sol +++ b/contracts/upgradeables/ercs/extensions/Achievo721SoulboundUpgradeable.sol @@ -2,8 +2,7 @@ pragma solidity ^0.8.17; /** - * Author: Omar (https://github.com/ogarciarevett) - * Co-Authors: Max (https://github.com/vasinl124) + * Author: Achievo Team - (https://achievo.xyz/) */ // MMMMNkc. .,oKWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM diff --git a/contracts/upgradeables/games/AvatarBoundV1.sol b/contracts/upgradeables/games/AvatarBoundV1.sol index 7a6c9e1d..92917eb5 100644 --- a/contracts/upgradeables/games/AvatarBoundV1.sol +++ b/contracts/upgradeables/games/AvatarBoundV1.sol @@ -2,8 +2,7 @@ pragma solidity ^0.8.17; /** - * Author: Omar (https://github.com/ogarciarevett) - * Co-Authors: Max (https://github.com/vasinl124) + * Author: Achievo Team - (https://achievo.xyz/) */ // MMMMNkc. .,oKWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM diff --git a/contracts/upgradeables/games/LevelsBoundV1.sol b/contracts/upgradeables/games/LevelsBoundV1.sol index 0a4111e3..4871f246 100644 --- a/contracts/upgradeables/games/LevelsBoundV1.sol +++ b/contracts/upgradeables/games/LevelsBoundV1.sol @@ -2,8 +2,7 @@ pragma solidity ^0.8.17; /** - * Author: Omar (https://github.com/ogarciarevett) - * Co-Authors: Max (https://github.com/vasinl124) + * Author: Achievo Team - (https://achievo.xyz/) */ // MMMMNkc. .,oKWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM diff --git a/contracts/upgradeables/games/LevelsBoundV2.sol b/contracts/upgradeables/games/LevelsBoundV2.sol index ca7e8fa2..a0a23ff3 100644 --- a/contracts/upgradeables/games/LevelsBoundV2.sol +++ b/contracts/upgradeables/games/LevelsBoundV2.sol @@ -2,8 +2,7 @@ pragma solidity ^0.8.17; /** - * Author: Omar (https://github.com/ogarciarevett) - * Co-Authors: Max (https://github.com/vasinl124) + * Author: Achievo Team - (https://achievo.xyz/) */ // MMMMNkc. .,oKWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM diff --git a/contracts/upgradeables/soulbounds/ERC1155RewardSoulboundV1.sol b/contracts/upgradeables/soulbounds/ERC1155RewardSoulboundV1.sol index 82d15c69..795bad2c 100644 --- a/contracts/upgradeables/soulbounds/ERC1155RewardSoulboundV1.sol +++ b/contracts/upgradeables/soulbounds/ERC1155RewardSoulboundV1.sol @@ -2,13 +2,11 @@ pragma solidity ^0.8.17; /** - * Author: Omar (https://github.com/ogarciarevett) - * Co-Authors: Max (https://github.com/vasinl124) + * Author: Achievo Team - (https://achievo.xyz/) */ //TODO: This contract is deprecated USE THE LootDrop.sol - // MMMMNkc. .,oKWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM // MWXd,. .cONMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM // Wx' .cKMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM @@ -200,10 +198,7 @@ contract ERC1155RewardSoulboundV1 is _burn(to, defaultRewardId, 1); } - function adminClaimERC20RewardById( - address to, - uint256[] calldata _tokenIds - ) public onlyRole(MANAGER_ROLE) { + function adminClaimERC20RewardById(address to, uint256[] calldata _tokenIds) public onlyRole(MANAGER_ROLE) { require(to != address(0), "InvalidToAddress"); for (uint256 i = 0; i < _tokenIds.length; i++) { diff --git a/contracts/upgradeables/soulbounds/ERC1155RoyaltiesSoulboundV1.sol b/contracts/upgradeables/soulbounds/ERC1155RoyaltiesSoulboundV1.sol index bf88693d..57605be8 100644 --- a/contracts/upgradeables/soulbounds/ERC1155RoyaltiesSoulboundV1.sol +++ b/contracts/upgradeables/soulbounds/ERC1155RoyaltiesSoulboundV1.sol @@ -2,13 +2,11 @@ pragma solidity ^0.8.17; /** - * Author: Max vasinl124(https://github.com/vasinl124) - * Co-Authors: Omar ogarciarevett(https://github.com/ogarciarevett) + * Author: Achievo Team - (https://achievo.xyz/) */ //TODO: This contract is deprecated USE THE ERC1155SoulboundV1.sol - // MMMMNkc. .,oKWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM // MWXd,. .cONMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM // Wx' .cKMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM diff --git a/contracts/upgradeables/soulbounds/ERC1155RoyaltiesSoulboundV2.sol b/contracts/upgradeables/soulbounds/ERC1155RoyaltiesSoulboundV2.sol index 94c88b5a..1c613298 100644 --- a/contracts/upgradeables/soulbounds/ERC1155RoyaltiesSoulboundV2.sol +++ b/contracts/upgradeables/soulbounds/ERC1155RoyaltiesSoulboundV2.sol @@ -2,8 +2,7 @@ pragma solidity ^0.8.17; /** - * Author: Max vasinl124(https://github.com/vasinl124) - * Co-Authors: Omar ogarciarevett(https://github.com/ogarciarevett) + * Author: Achievo Team - (https://achievo.xyz/) */ //TODO: This contract is deprecated USE THE ERC1155SoulboundV1.sol diff --git a/contracts/upgradeables/soulbounds/ERC1155SoulboundV1.sol b/contracts/upgradeables/soulbounds/ERC1155SoulboundV1.sol index 11338861..6901f4cc 100644 --- a/contracts/upgradeables/soulbounds/ERC1155SoulboundV1.sol +++ b/contracts/upgradeables/soulbounds/ERC1155SoulboundV1.sol @@ -2,8 +2,7 @@ pragma solidity ^0.8.17; /** - * Author: Max vasinl124(https://github.com/vasinl124) - * Co-Authors: Omar ogarciarevett(https://github.com/ogarciarevett) + * Author: Achievo Team - (https://achievo.xyz/) */ // MMMMNkc. .,oKWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM diff --git a/contracts/upgradeables/soulbounds/ERC721SoulboundV1.sol b/contracts/upgradeables/soulbounds/ERC721SoulboundV1.sol index af2e1ba8..9ed349d1 100644 --- a/contracts/upgradeables/soulbounds/ERC721SoulboundV1.sol +++ b/contracts/upgradeables/soulbounds/ERC721SoulboundV1.sol @@ -2,8 +2,7 @@ pragma solidity ^0.8.17; /** - * Author: Omar (https://github.com/ogarciarevett) - * Co-Authors: Max (https://github.com/vasinl124) + * Author: Achievo Team - (https://achievo.xyz/) */ // MMMMNkc. .,oKWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM @@ -38,7 +37,6 @@ import { } from "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol"; import { Achievo721SoulboundUpgradeable } from "../ercs/extensions/Achievo721SoulboundUpgradeable.sol"; - contract ERC721SoulboundV1 is Initializable, ERC721URIStorageUpgradeable, diff --git a/tasks/deploy-create2.ts b/tasks/deploy-create2.ts index ea729ae2..30d8eb5d 100644 --- a/tasks/deploy-create2.ts +++ b/tasks/deploy-create2.ts @@ -28,9 +28,8 @@ const deployOne = async ( ): Promise => { const encryptedPrivateKey = await encryptPrivateKey(DETERMINISTIC_DEPLOYER_PRIVATE_KEY); - const abiPath = getABIFilePath(true, contractFileName); - const isZkSync = networkName.toLowerCase().includes('zksync'); + const abiPath = getABIFilePath(isZkSync, contractFileName); const networkNameKey = Object.keys(NetworkName)[Object.values(NetworkName).indexOf(networkName as NetworkName)]; const chainId = ChainId[networkNameKey as keyof typeof ChainId]; @@ -51,13 +50,10 @@ const deployOne = async ( Object.keys(NetworkName)[Object.values(NetworkName).indexOf(ethNetworkName as NetworkName)]; const ethChainId = ChainId[ethNetworkNameKey as keyof typeof ChainId]; const ethRpcUrl = rpcUrls[ethChainId]; - const provider = new zkProvider(rpcUrl); const ethProvider = hre.ethers.getDefaultProvider(ethRpcUrl); - deployerWallet = new zkWallet(DETERMINISTIC_DEPLOYER_PRIVATE_KEY, provider, ethProvider); managerWallet = new zkWallet(PRIVATE_KEY, provider, ethProvider); - const factory = new zkContractFactory(contractAbi, bytecode, deployerWallet); achievoContract = await factory.deploy(managerWallet.address); } else { diff --git a/test/games/GameSummary.t.sol b/test/games/GameSummary.t.sol new file mode 100644 index 00000000..cfb5b5b3 --- /dev/null +++ b/test/games/GameSummary.t.sol @@ -0,0 +1,773 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import "forge-std/Test.sol"; +import "forge-std/StdCheats.sol"; +import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; +import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; +import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; + +import { GameSummary } from "../../contracts/games/GameSummary.sol"; +import { MockERC1155Receiver } from "../../contracts/mocks/MockERC1155Receiver.sol"; +import { LibGameSummary } from "../../contracts/libraries/LibGameSummary.sol"; + +contract GameSummaryBoundTest is StdCheats, Test { + using Strings for uint256; + + GameSummary public gameSummary; + MockERC1155Receiver public mockERC1155Receiver; + + struct Wallet { + address addr; + uint256 privateKey; + } + + string public minterLabel = "minter"; + string public playerLabel = "player"; + string public player2Label = "player2"; + string public player3Label = "player3"; + + Wallet public minterWallet; + Wallet public playerWallet; + Wallet public playerWallet2; + Wallet public playerWallet3; + + uint256 public seed1 = 1234; + uint256 public seed2 = 4321; + uint256 public nonce; + bytes public signature; + bytes public encodedItems1; + uint256 public nonce2; + bytes public signature2; + bytes public encodedItems2; + + uint256 private _seed; + LibGameSummary.GameSummaryCreate[] public _tokens; + uint256[] public _tokenIds; + uint256[] public _storeIds; + uint256[] public _playerIds; + uint256[] public _gameIds; + + uint256 public chainId = 31337; + + function getWallet(string memory walletLabel) public returns (Wallet memory) { + (address addr, uint256 privateKey) = makeAddrAndKey(walletLabel); + Wallet memory wallet = Wallet(addr, privateKey); + return wallet; + } + + function generateSignature( + address wallet, + bytes memory encodedItems, + string memory signerLabel + ) public returns (uint256, bytes memory) { + Wallet memory signerWallet = getWallet(signerLabel); + + uint256 _nonce = uint256(keccak256(abi.encodePacked(block.timestamp, block.prevrandao, signerWallet.addr))) % + 50; + + bytes32 message = keccak256(abi.encodePacked(wallet, encodedItems, _nonce)); + bytes32 hash = ECDSA.toEthSignedMessageHash(message); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign(signerWallet.privateKey, hash); + return (_nonce, abi.encodePacked(r, s, v)); + } + + function concatenateStrings(string memory a, string memory b) internal pure returns (string memory) { + return string(abi.encodePacked(a, b)); + } + + function generateRandomStoreId() internal returns (uint256) { + _seed = uint256(keccak256(abi.encodePacked(blockhash(block.number - 1), _seed))); + return _seed; + } + + function generateRandomPlayerId() internal returns (uint256) { + uint256 _seed = uint256(keccak256(abi.encodePacked(blockhash(block.number - 1), _seed))); + return _seed % 1000000; + } + + function generateRandomGameId() internal returns (uint256) { + uint256 _seed = uint256(keccak256(abi.encodePacked(blockhash(block.number - 1), _seed))); + return _seed % 10000000; + } + + function generateTokenId(uint256 storeId, uint256 playerId, uint256 gameId) internal returns (uint256) { + uint256 tokenId = uint256(keccak256(abi.encode(storeId, playerId, gameId))); + return tokenId; + } + + function encode( + address contractAddress, + uint256[] memory storeIds, + uint256[] memory playerIds, + uint256[] memory gameIds + ) public view returns (bytes memory) { + return (abi.encode(contractAddress, chainId, _storeIds, _playerIds, _gameIds)); + } + + function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { + bytes memory buffer = new bytes(2 * length + 2); + buffer[0] = "0"; + buffer[1] = "x"; + for (uint256 i = 2 * length + 1; i > 1; --i) { + buffer[i] = bytes1(uint8(value & 0xf)); + value >>= 4; + if (i % 2 == 0) { + buffer[i] = bytes1(uint8(buffer[i]) + (bytes1(uint8(buffer[i])) < bytes1(uint8(0xa)) ? 48 : 87)); + } + } + return string(buffer); + } + function setUp() public { + playerWallet = getWallet(playerLabel); + playerWallet2 = getWallet(player2Label); + playerWallet3 = getWallet(player3Label); + minterWallet = getWallet(minterLabel); + + gameSummary = new GameSummary(address(this)); + + gameSummary.initialize( + "Test1155", + "T1155", + "MISSING_BASE_URL", + "MISSING_CONTRACT_URL", + "https://example.api.com", + 1, + false, + address(this) + ); + + gameSummary.addWhitelistSigner(minterWallet.addr); + + mockERC1155Receiver = new MockERC1155Receiver(); + + for (uint256 i = 0; i < 1300; i++) { + uint256 _storeId = generateRandomStoreId(); + uint256 _playerId = generateRandomPlayerId(); + uint256 _gameId = generateRandomGameId(); + uint256 _tokenId = generateTokenId(_storeId, _playerId, _gameId); + + _storeIds.push(_storeId); + _playerIds.push(_playerId); + _gameIds.push(_gameId); + _tokenIds.push(_tokenId); + } + + uint256[] memory _storeIds1 = new uint256[](3); + _storeIds1[0] = _storeIds[0]; + _storeIds1[1] = _storeIds[1]; + _storeIds1[2] = _storeIds[2]; + + uint256[] memory _playerIds1 = new uint256[](3); + _playerIds1[0] = _playerIds[0]; + _playerIds1[1] = _playerIds[1]; + _playerIds1[2] = _playerIds[2]; + + uint256[] memory _gameIds1 = new uint256[](3); + _gameIds1[0] = _gameIds[0]; + _gameIds1[1] = _gameIds[1]; + _gameIds1[2] = _gameIds[2]; + + encodedItems1 = encode(address(gameSummary), _storeIds1, _playerIds1, _gameIds1); + + uint256[] memory _storeIds2 = new uint256[](3); + _storeIds2[0] = _storeIds[3]; + _storeIds2[1] = _storeIds[4]; + _storeIds2[2] = _storeIds[5]; + + uint256[] memory _playerIds2 = new uint256[](3); + _playerIds2[0] = _playerIds[3]; + _playerIds2[1] = _playerIds[4]; + _playerIds2[2] = _playerIds[5]; + + uint256[] memory _gameIds2 = new uint256[](3); + _gameIds2[0] = _gameIds[3]; + _gameIds2[1] = _gameIds[4]; + _gameIds2[2] = _gameIds[5]; + + encodedItems2 = encode(address(gameSummary), _storeIds2, _playerIds2, _gameIds2); + + (nonce, signature) = generateSignature(playerWallet.addr, encodedItems1, minterLabel); + (nonce2, signature2) = generateSignature(playerWallet2.addr, encodedItems2, minterLabel); + } + + function testTokenExists() public { + uint256 _storeId = generateRandomStoreId(); + uint256 _playerId = generateRandomPlayerId(); + uint256 _gameId = generateRandomGameId(); + uint256 _tokenId = generateTokenId(_storeId, _playerId, _gameId); + + assertEq(gameSummary.isTokenExist(_tokenId), false); + + gameSummary.adminMintId(playerWallet.addr, _storeId, _playerId, _gameId, 1, true); + assertEq(gameSummary.isTokenExist(_tokenId), true); + } + + function testPauseUnpause() public { + uint256 _storeId = _storeIds[0]; + uint256 _playerId = _playerIds[0]; + uint256 _gameId = _gameIds[0]; + uint256 _tokenId = gameSummary.getTokenId(_storeId, _playerId, _gameId); + + gameSummary.pause(); + vm.expectRevert("Pausable: paused"); + gameSummary.adminMintId(address(this), _storeId, _playerId, _gameId, 1, true); + gameSummary.unpause(); + + gameSummary.adminMintId(address(mockERC1155Receiver), _storeId, _playerId, _gameId, 1, true); + assertEq(gameSummary.balanceOf(address(mockERC1155Receiver), _tokenId), 1); + } + + function testPauseUnpauseSpecificToken() public { + uint256 _storeId = _storeIds[0]; + uint256 _playerId = _playerIds[0]; + uint256 _gameId = _gameIds[0]; + uint256 _tokenId = gameSummary.getTokenId(_storeId, _playerId, _gameId); + + gameSummary.updateTokenMintPaused(_tokenId, true); + + vm.expectRevert("TokenMintPaused"); + gameSummary.adminMintId(address(mockERC1155Receiver), _storeId, _playerId, _gameId, 1, true); + + vm.expectRevert("TokenMintPaused"); + gameSummary.adminMint(address(mockERC1155Receiver), encodedItems1, true); + + vm.expectRevert("TokenMintPaused"); + vm.prank(playerWallet.addr); + gameSummary.mint(encodedItems1, 1, true, nonce, signature); + + gameSummary.updateTokenMintPaused(_tokenId, false); + + vm.prank(playerWallet.addr); + gameSummary.mint(encodedItems1, 1, true, nonce, signature); + + assertEq(gameSummary.balanceOf(playerWallet.addr, _tokenId), 1); + } + + // testVerifySignature + function testInvalidSignature() public { + vm.prank(playerWallet.addr); + vm.expectRevert("InvalidSignature"); + gameSummary.mint(encodedItems1, 1, true, nonce, signature2); + } + + function testReuseSignatureMint() public { + vm.prank(playerWallet.addr); + gameSummary.mint(encodedItems1, 1, true, nonce, signature); + vm.prank(playerWallet.addr); + vm.expectRevert("AlreadyUsedSignature"); + gameSummary.mint(encodedItems1, 1, true, nonce, signature); + } + + function testMintShouldPass() public { + vm.prank(playerWallet.addr); + gameSummary.mint(encodedItems1, 1, true, nonce, signature); + + vm.expectRevert( + "Achievo1155Soulbound: The amount of soulbounded tokens is more than the amount of tokens to be transferred" + ); + vm.prank(playerWallet.addr); + gameSummary.safeTransferFrom(playerWallet.addr, minterWallet.addr, _tokenIds[0], 1, ""); + + vm.expectRevert("Achievo1155Soulbound: can't be zero amount"); + vm.prank(playerWallet.addr); + gameSummary.safeTransferFrom(playerWallet.addr, minterWallet.addr, _tokenIds[0], 0, ""); + + vm.prank(playerWallet2.addr); + gameSummary.mint(encodedItems2, 1, false, nonce2, signature2); + + vm.prank(playerWallet2.addr); + gameSummary.safeTransferFrom(playerWallet2.addr, minterWallet.addr, _tokenIds[3], 1, ""); + + assertEq(gameSummary.balanceOf(playerWallet.addr, _tokenIds[0]), 1); + assertEq(gameSummary.balanceOf(playerWallet2.addr, _tokenIds[3]), 0); + assertEq(gameSummary.balanceOf(minterWallet.addr, _tokenIds[3]), 1); + } + + function testMintMoreThanOneTokenPerWallet() public { + uint256 _storeId = _storeIds[0]; + uint256 _playerId = _playerIds[0]; + uint256 _gameId = _gameIds[0]; + uint256 _tokenId = gameSummary.getTokenId(_storeId, _playerId, _gameId); + + gameSummary.adminMintId(address(mockERC1155Receiver), _storeId, _playerId, _gameId, 1, true); + assertEq(gameSummary.balanceOf(address(mockERC1155Receiver), _tokenId), 1); + + vm.expectRevert("AlreadyMinted"); + gameSummary.adminMintId(address(mockERC1155Receiver), _storeId, _playerId, _gameId, 1, true); + } + + function testMintMoreThanLimit() public { + vm.expectRevert("ExceedMaxMint"); + vm.prank(playerWallet.addr); + gameSummary.mint(encodedItems1, 2, true, nonce, signature); + } + + function testAdminMintNotMinterRole() public { + vm.expectRevert( + "AccessControl: account 0x44e97af4418b7a17aabd8090bea0a471a366305c is missing role 0x9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6" + ); + vm.prank(playerWallet.addr); + gameSummary.adminMint(playerWallet.addr, encodedItems1, true); + } + + function testAdminMint() public { + gameSummary.adminMint(address(mockERC1155Receiver), encodedItems1, true); + assertEq(gameSummary.balanceOf(address(mockERC1155Receiver), _tokenIds[0]), 1); + assertEq(gameSummary.balanceOf(address(mockERC1155Receiver), _tokenIds[1]), 1); + assertEq(gameSummary.balanceOf(address(mockERC1155Receiver), _tokenIds[2]), 1); + } + + function testAdminMintIdNotMinterRole() public { + uint256 _storeId = _storeIds[0]; + uint256 _playerId = _playerIds[0]; + uint256 _gameId = _gameIds[0]; + uint256 _tokenId = gameSummary.getTokenId(_storeId, _playerId, _gameId); + + vm.expectRevert( + "AccessControl: account 0x44e97af4418b7a17aabd8090bea0a471a366305c is missing role 0x9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6" + ); + vm.prank(playerWallet.addr); + gameSummary.adminMintId(playerWallet.addr, _storeId, _playerId, _gameId, 1, true); + } + + function testAdminMintId() public { + uint256 _storeId = _storeIds[0]; + uint256 _playerId = _playerIds[0]; + uint256 _gameId = _gameIds[0]; + uint256 _tokenId = gameSummary.getTokenId(_storeId, _playerId, _gameId); + + gameSummary.adminMintId(playerWallet.addr, _storeId, _playerId, _gameId, 1, true); + assertEq(gameSummary.balanceOf(playerWallet.addr, _tokenIds[0]), 1); + } + + function testBurnNotOwnerShouldFail() public { + vm.prank(playerWallet.addr); + gameSummary.mint(encodedItems1, 1, false, nonce, signature); + assertEq(gameSummary.balanceOf(playerWallet.addr, _tokenIds[0]), 1); + + vm.expectRevert("ERC1155: caller is not token owner or approved"); + vm.prank(playerWallet2.addr); + gameSummary.burn(playerWallet.addr, _tokenIds[0], 1); + } + + function testBurn() public { + vm.prank(playerWallet.addr); + gameSummary.mint(encodedItems1, 1, true, nonce, signature); + assertEq(gameSummary.balanceOf(playerWallet.addr, _tokenIds[0]), 1); + + vm.expectRevert( + "Achievo1155Soulbound: The amount of soulbounded tokens is more than the amount of tokens to be transferred" + ); + vm.prank(playerWallet.addr); + gameSummary.safeTransferFrom(playerWallet.addr, minterWallet.addr, _tokenIds[0], 1, ""); + + vm.expectRevert( + "Achievo1155Soulbound: The amount of soulbounded tokens is more than the amount of tokens to be transferred" + ); + vm.prank(playerWallet.addr); + gameSummary.burn(playerWallet.addr, _tokenIds[0], 1); + + vm.prank(playerWallet2.addr); + gameSummary.mint(encodedItems2, 1, false, nonce2, signature2); + + vm.prank(playerWallet2.addr); + gameSummary.safeTransferFrom(playerWallet2.addr, playerWallet3.addr, _tokenIds[3], 1, ""); + + assertEq(gameSummary.balanceOf(playerWallet2.addr, _tokenIds[3]), 0); + assertEq(gameSummary.balanceOf(playerWallet3.addr, _tokenIds[3]), 1); + + vm.prank(playerWallet3.addr); + gameSummary.burn(playerWallet3.addr, _tokenIds[3], 1); + + assertEq(gameSummary.balanceOf(playerWallet3.addr, _tokenIds[3]), 0); + } + + function testBurnIfHoldBothNonSoulboundAndSouldbound() public { + vm.prank(playerWallet.addr); + gameSummary.mint(encodedItems1, 1, true, nonce, signature); + + gameSummary.adminMint(playerWallet2.addr, encodedItems1, false); + + vm.prank(playerWallet2.addr); + gameSummary.safeTransferFrom(playerWallet2.addr, playerWallet.addr, _tokenIds[0], 1, ""); + + assertEq(gameSummary.balanceOf(playerWallet.addr, _tokenIds[0]), 2); + + vm.expectRevert( + "Achievo1155Soulbound: The amount of soulbounded tokens is more than the amount of tokens to be transferred" + ); + vm.prank(playerWallet.addr); + gameSummary.safeTransferFrom(playerWallet.addr, minterWallet.addr, _tokenIds[0], 2, ""); + + vm.prank(playerWallet.addr); + gameSummary.safeTransferFrom(playerWallet.addr, minterWallet.addr, _tokenIds[0], 1, ""); + } + + function testBurnBatchNotOwnerShouldFail() public { + uint256[] memory _itemIds1 = new uint256[](3); + _itemIds1[0] = _tokenIds[0]; + _itemIds1[1] = _tokenIds[1]; + _itemIds1[2] = _tokenIds[2]; + + uint256[] memory _amount1 = new uint256[](3); + _amount1[0] = 1; + _amount1[1] = 1; + _amount1[2] = 1; + + vm.prank(playerWallet.addr); + gameSummary.mint(encodedItems1, 1, false, nonce, signature); + assertEq(gameSummary.balanceOf(playerWallet.addr, _tokenIds[0]), 1); + + vm.expectRevert("ERC1155: caller is not token owner or approved"); + vm.prank(playerWallet2.addr); + gameSummary.burnBatch(playerWallet.addr, _itemIds1, _amount1); + } + + function testBurnBatch() public { + uint256[] memory _itemIds1 = new uint256[](3); + _itemIds1[0] = _tokenIds[0]; + _itemIds1[1] = _tokenIds[1]; + _itemIds1[2] = _tokenIds[2]; + + uint256[] memory _itemIds2 = new uint256[](3); + _itemIds2[0] = _tokenIds[3]; + _itemIds2[1] = _tokenIds[4]; + _itemIds2[2] = _tokenIds[5]; + + uint256[] memory _amount1 = new uint256[](3); + _amount1[0] = 1; + _amount1[1] = 1; + _amount1[2] = 1; + + vm.prank(playerWallet.addr); + gameSummary.mint(encodedItems1, 1, true, nonce, signature); + assertEq(gameSummary.balanceOf(playerWallet.addr, _tokenIds[0]), 1); + + vm.expectRevert( + "Achievo1155Soulbound: The amount of soulbounded tokens is more than the amount of tokens to be transferred" + ); + vm.prank(playerWallet.addr); + gameSummary.safeTransferFrom(playerWallet.addr, minterWallet.addr, _tokenIds[0], 1, ""); + + vm.expectRevert( + "Achievo1155Soulbound: The amount of soulbounded tokens is more than the amount of tokens to be transferred" + ); + vm.prank(playerWallet.addr); + gameSummary.burnBatch(playerWallet.addr, _itemIds1, _amount1); + + vm.prank(playerWallet2.addr); + gameSummary.mint(encodedItems2, 1, false, nonce2, signature2); + + vm.prank(playerWallet2.addr); + gameSummary.safeTransferFrom(playerWallet2.addr, playerWallet3.addr, _tokenIds[3], 1, ""); + vm.prank(playerWallet2.addr); + gameSummary.safeTransferFrom(playerWallet2.addr, playerWallet3.addr, _tokenIds[4], 1, ""); + vm.prank(playerWallet2.addr); + gameSummary.safeTransferFrom(playerWallet2.addr, playerWallet3.addr, _tokenIds[5], 1, ""); + + assertEq(gameSummary.balanceOf(playerWallet2.addr, _tokenIds[3]), 0); + assertEq(gameSummary.balanceOf(playerWallet3.addr, _tokenIds[3]), 1); + + vm.prank(playerWallet3.addr); + gameSummary.burnBatch(playerWallet3.addr, _itemIds2, _amount1); + + assertEq(gameSummary.balanceOf(playerWallet3.addr, _tokenIds[3]), 0); + } + + function testBatchTransferFrom() public { + uint256[] memory _itemIds1 = new uint256[](3); + _itemIds1[0] = _tokenIds[0]; + _itemIds1[1] = _tokenIds[1]; + _itemIds1[2] = _tokenIds[2]; + + uint256[] memory _itemIds2 = new uint256[](3); + _itemIds2[0] = _tokenIds[3]; + _itemIds2[1] = _tokenIds[4]; + _itemIds2[2] = _tokenIds[5]; + + uint256[] memory _amount1 = new uint256[](3); + _amount1[0] = 1; + _amount1[1] = 1; + _amount1[2] = 1; + + vm.prank(playerWallet.addr); + gameSummary.mint(encodedItems1, 1, true, nonce, signature); + assertEq(gameSummary.balanceOf(playerWallet.addr, _tokenIds[0]), 1); + + gameSummary.adminMint(playerWallet2.addr, encodedItems1, false); + + vm.prank(playerWallet2.addr); + gameSummary.safeTransferFrom(playerWallet2.addr, playerWallet.addr, _tokenIds[0], 1, ""); + + assertEq(gameSummary.balanceOf(playerWallet.addr, _tokenIds[0]), 2); + + uint256[] memory _itemIds3 = new uint256[](2); + _itemIds3[0] = _tokenIds[0]; + _itemIds3[1] = _tokenIds[0]; + + uint256[] memory _amount3 = new uint256[](2); + _amount3[0] = 1; + _amount3[1] = 1; + + vm.expectRevert("ERC1155: duplicate ID"); + vm.prank(playerWallet.addr); + gameSummary.safeBatchTransferFrom(playerWallet.addr, minterWallet.addr, _itemIds3, _amount3, ""); + + assertEq(gameSummary.balanceOf(minterWallet.addr, _tokenIds[0]), 0); + + vm.prank(playerWallet.addr); + gameSummary.safeTransferFrom(playerWallet.addr, minterWallet.addr, _tokenIds[0], 1, ""); + assertEq(gameSummary.balanceOf(minterWallet.addr, _tokenIds[0]), 1); + } + + function testTokenURIIfTokenIdNotExist() public { + vm.expectRevert("TokenNotExist"); + gameSummary.uri(1); + } + + function testTokenURIIfTokenIdExistNOSpeficTokenURIFallbackToBaseURI() public { + uint256 _storeId = generateRandomStoreId(); + uint256 _playerId = generateRandomPlayerId(); + uint256 _gameId = generateRandomGameId(); + uint256 _tokenId = generateTokenId(_storeId, _playerId, _gameId); + + gameSummary.adminMintId(playerWallet.addr, _storeId, _playerId, _gameId, 1, false); + gameSummary.setCompoundURIEnabled(false); + assertEq(gameSummary.uri(_tokenId), string(abi.encodePacked("MISSING_BASE_URL", "/", _tokenId.toString()))); + } + + function testUpdateTokenBaseURIFailNotDevConfigRole() public { + string memory newBaseURI = "https://something-new.com"; + + vm.expectRevert( + "AccessControl: account 0x44e97af4418b7a17aabd8090bea0a471a366305c is missing role 0x3b359cf0b4471a5de84269135285268e64ac56f52d3161392213003a780ad63b" + ); + vm.prank(playerWallet.addr); + gameSummary.setBaseUri(newBaseURI); + } + + function testUpdateTokenBaseURIPass() public { + uint256 _storeId = generateRandomStoreId(); + uint256 _playerId = generateRandomPlayerId(); + uint256 _gameId = generateRandomGameId(); + uint256 _tokenId = generateTokenId(_storeId, _playerId, _gameId); + + gameSummary.adminMintId(playerWallet.addr, _storeId, _playerId, _gameId, 1, false); + gameSummary.setCompoundURIEnabled(false); + + string memory newBaseURI = "https://something-new.com"; + + assertEq(gameSummary.uri(_tokenId), string(abi.encodePacked("MISSING_BASE_URL", "/", _tokenId.toString()))); + gameSummary.setBaseUri(newBaseURI); + assertEq( + gameSummary.uri(_tokenId), + string(abi.encodePacked("https://something-new.com", "/", _tokenId.toString())) + ); + } + + function testUpdateCompountURIPass() public { + uint256 _storeId = generateRandomStoreId(); + uint256 _playerId = generateRandomPlayerId(); + uint256 _gameId = generateRandomGameId(); + uint256 _tokenId = generateTokenId(_storeId, _playerId, _gameId); + + gameSummary.adminMintId(playerWallet.addr, _storeId, _playerId, _gameId, 1, false); + + string memory newCompoundUri = "https://something-new.com/232"; + + assertEq( + gameSummary.uri(_tokenId), + string( + abi.encodePacked( + "https://example.api.com/", + Strings.toHexString(uint160(address(gameSummary)), 20), + "/", + Strings.toString(_storeId), + "-", + Strings.toString(_playerId), + "-", + Strings.toString(_gameId) + ) + ) + ); + + gameSummary.setCompoundURI(newCompoundUri); + + assertEq( + gameSummary.uri(_tokenId), + string( + abi.encodePacked( + "https://something-new.com/232/", + Strings.toHexString(uint160(address(gameSummary)), 20), + "/", + Strings.toString(_storeId), + "-", + Strings.toString(_playerId), + "-", + Strings.toString(_gameId) + ) + ) + ); + } + + function testNonSoulboundTokenTransfer() public { + uint256 _storeId = _storeIds[0]; + uint256 _playerId = _playerIds[0]; + uint256 _gameId = _gameIds[0]; + uint256 _tokenId = gameSummary.getTokenId(_storeId, _playerId, _gameId); + gameSummary.adminMintId(playerWallet.addr, _storeId, _playerId, _gameId, 1, false); + + vm.prank(playerWallet.addr); + gameSummary.safeTransferFrom(playerWallet.addr, minterWallet.addr, _tokenId, 1, ""); + + assertEq(gameSummary.balanceOf(playerWallet.addr, _tokenId), 0); + assertEq(gameSummary.balanceOf(minterWallet.addr, _tokenId), 1); + } + + function testSoulboundTokenNotTransfer() public { + uint256 _storeId = _storeIds[0]; + uint256 _playerId = _playerIds[0]; + uint256 _gameId = _gameIds[0]; + uint256 _tokenId = gameSummary.getTokenId(_storeId, _playerId, _gameId); + gameSummary.adminMintId(playerWallet.addr, _storeId, _playerId, _gameId, 1, true); + + vm.expectRevert( + "Achievo1155Soulbound: The amount of soulbounded tokens is more than the amount of tokens to be transferred" + ); + vm.prank(playerWallet.addr); + gameSummary.safeTransferFrom(playerWallet.addr, minterWallet.addr, _tokenId, 1, ""); + + vm.expectRevert("Achievo1155Soulbound: can't be zero amount"); + vm.prank(playerWallet.addr); + gameSummary.safeTransferFrom(playerWallet.addr, minterWallet.addr, _tokenId, 0, ""); + } + + function testSoulboundTokenTransferOnlyWhitelistAddresses() public { + uint256 _storeId = _storeIds[0]; + uint256 _playerId = _playerIds[0]; + uint256 _gameId = _gameIds[0]; + uint256 _tokenId = gameSummary.getTokenId(_storeId, _playerId, _gameId); + gameSummary.adminMintId(playerWallet.addr, _storeId, _playerId, _gameId, 1, true); + + vm.expectRevert( + "Achievo1155Soulbound: The amount of soulbounded tokens is more than the amount of tokens to be transferred" + ); + vm.prank(playerWallet.addr); + gameSummary.safeTransferFrom(playerWallet.addr, playerWallet3.addr, _tokenId, 1, ""); + + gameSummary.updateWhitelistAddress(playerWallet3.addr, true); + + vm.prank(playerWallet.addr); + gameSummary.safeTransferFrom(playerWallet.addr, playerWallet3.addr, _tokenId, 1, ""); + + vm.prank(playerWallet3.addr); + gameSummary.safeTransferFrom(playerWallet3.addr, playerWallet.addr, _tokenId, 1, ""); + + gameSummary.updateWhitelistAddress(playerWallet3.addr, false); + + vm.expectRevert( + "Achievo1155Soulbound: The amount of soulbounded tokens is more than the amount of tokens to be transferred" + ); + vm.prank(playerWallet.addr); + gameSummary.safeTransferFrom(playerWallet.addr, playerWallet3.addr, _tokenId, 1, ""); + } + + function testgetAllItems() public { + gameSummary.setCompoundURIEnabled(false); + bytes memory encodedItemsAll = encode(address(gameSummary), _storeIds, _playerIds, _gameIds); + gameSummary.adminMint(playerWallet.addr, encodedItemsAll, false); + + vm.prank(playerWallet.addr); + LibGameSummary.GameSummaryReturn[] memory allTokensInfo = gameSummary.getAllItems(); + assertEq(allTokensInfo.length, 1300); + + vm.prank(playerWallet.addr); + gameSummary.safeTransferFrom(playerWallet.addr, minterWallet.addr, _tokenIds[14], 1, ""); + + vm.prank(playerWallet.addr); + LibGameSummary.GameSummaryReturn[] memory allTokensInfo2 = gameSummary.getAllItems(); + assertEq(allTokensInfo2.length, 1299); + + for (uint256 i = 0; i < allTokensInfo.length; i++) { + assertEq(allTokensInfo[i].tokenId, _tokenIds[i]); + + assertEq(allTokensInfo[i].amount, 1); + assertEq(allTokensInfo[i].tokenUri, string(abi.encodePacked("MISSING_BASE_URL/", _tokenIds[i].toString()))); + } + + gameSummary.setCompoundURIEnabled(true); + + LibGameSummary.GameSummaryReturn[] memory allTokensInfoAfter = gameSummary.getAllItemsAdmin(playerWallet.addr); + for (uint256 i = 0; i < allTokensInfoAfter.length; i++) { + assertEq(allTokensInfoAfter[i].tokenId, _tokenIds[i]); + if (i != 14) { + assertEq(allTokensInfoAfter[i].amount, 1); + } + assertEq( + allTokensInfoAfter[i].tokenUri, + string( + abi.encodePacked( + "https://example.api.com/", + Strings.toHexString(uint160(address(gameSummary)), 20), + "/", + Strings.toString(_storeIds[i]), + "-", + Strings.toString(_playerIds[i]), + "-", + Strings.toString(_gameIds[i]) + ) + ) + ); + } + + vm.prank(minterWallet.addr); + LibGameSummary.GameSummaryReturn[] memory allTokensInfo3 = gameSummary.getAllItems(); + assertEq(allTokensInfo3.length, 1); + } + + function testgetAllItemsAdmin() public { + gameSummary.setCompoundURIEnabled(false); + bytes memory encodedItemsAll = encode(address(gameSummary), _storeIds, _playerIds, _gameIds); + gameSummary.adminMint(playerWallet.addr, encodedItemsAll, false); + + LibGameSummary.GameSummaryReturn[] memory allTokensInfo = gameSummary.getAllItemsAdmin(playerWallet.addr); + assertEq(allTokensInfo.length, 1300); + + vm.prank(playerWallet.addr); + gameSummary.safeTransferFrom(playerWallet.addr, minterWallet.addr, _tokenIds[14], 1, ""); + + for (uint256 i = 0; i < allTokensInfo.length; i++) { + assertEq(allTokensInfo[i].tokenId, _tokenIds[i]); + + assertEq(allTokensInfo[i].amount, 1); + assertEq(allTokensInfo[i].tokenUri, string(abi.encodePacked("MISSING_BASE_URL/", _tokenIds[i].toString()))); + } + + gameSummary.setCompoundURIEnabled(true); + + LibGameSummary.GameSummaryReturn[] memory allTokensInfoAfter = gameSummary.getAllItemsAdmin(playerWallet.addr); + for (uint256 i = 0; i < allTokensInfoAfter.length; i++) { + assertEq(allTokensInfoAfter[i].tokenId, _tokenIds[i]); + if (i != 14) { + assertEq(allTokensInfoAfter[i].amount, 1); + } + assertEq( + allTokensInfoAfter[i].tokenUri, + string( + abi.encodePacked( + "https://example.api.com/", + Strings.toHexString(uint160(address(gameSummary)), 20), + "/", + Strings.toString(_storeIds[i]), + "-", + Strings.toString(_playerIds[i]), + "-", + Strings.toString(_gameIds[i]) + ) + ) + ); + } + } +} diff --git a/test/hardhatTests/gameSummary1155.test.ts b/test/hardhatTests/gameSummary1155.test.ts deleted file mode 100644 index e2ca07a6..00000000 --- a/test/hardhatTests/gameSummary1155.test.ts +++ /dev/null @@ -1,770 +0,0 @@ -import { expect } from 'chai'; -// @ts-ignore-next-line -import { ethers } from 'hardhat'; -import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; -import { GameSummary } from '../../typechain-types'; -import { generateSignature } from '../../helpers/signature'; -import { hashIds } from '../../helpers/hashing'; -import { log } from 'debug'; - -describe('GameSummary', function () { - let GameSummary: GameSummary; - let minterAccount: SignerWithAddress; - let playerAccount: SignerWithAddress; - const DEFAULT_GAME_ID = 11; - const DEFAULT_STORE_ID = 22; - const DEFAULT_TOKEN_ID = hashIds(DEFAULT_STORE_ID, DEFAULT_GAME_ID); - const defaultBaseURI = 'https://achievo.mypinata.cloud/ipfs/'; - let defaultCommonGameSummary: GameSummary.GameSummaryStruct; - beforeEach(async function () { - const contract = (await ethers.getContractFactory('GameSummary')) as unknown as GameSummary; - const [adminAccount, player] = await ethers.getSigners(); - minterAccount = adminAccount; - playerAccount = player; - // @ts-ignore-next-line - GameSummary = await contract.deploy(defaultBaseURI); - await GameSummary.waitForDeployment(); - const minterRole = await GameSummary.MINTER_ROLE(); - const gameCreatorRole = await GameSummary.GAME_CREATOR_ROLE(); - await GameSummary.grantRole(minterRole, minterAccount.address); - await GameSummary.grantRole(gameCreatorRole, minterAccount.address); - await ( - await GameSummary.createCommonGameSummary( - DEFAULT_STORE_ID, - DEFAULT_GAME_ID, - 'GameTest1234', - 'test://on_chain_uri', - 'external_uri', - 500 - ) - ).wait(); - - defaultCommonGameSummary = await GameSummary.getGameSummary(DEFAULT_TOKEN_ID); - }); - - it('As Player must mint game summary achievement', async function () { - const { signature, nonce } = await generateSignature({ - walletAddress: playerAccount.address, - signer: minterAccount, - }); - const whitelistTx = await GameSummary.setSigner(minterAccount.address); - await whitelistTx.wait(); - const randomAchievementIds = ['100_KILLS_WITH_AWP', 'WEAPON_MASTER', 'X_ACHIEVEMENT']; - const tx = await GameSummary.connect(playerAccount).mintGameSummaryWithSignature( - DEFAULT_GAME_ID, - randomAchievementIds.length, - DEFAULT_STORE_ID, - nonce, - signature - ); - await tx.wait(); - const tokenId = await GameSummary.getTokenId(DEFAULT_STORE_ID, DEFAULT_GAME_ID); - const balance = await GameSummary.balanceOf(playerAccount.address, tokenId.toString()); - expect(Number(balance)).to.equal(1); - }); - - it('As Admin must mint a game summary for a player', async function () { - const randomAchievementIds = [1234, 65441, 12312]; - const tx = await GameSummary.adminMintGameSummary( - playerAccount.address, - DEFAULT_GAME_ID, - randomAchievementIds.length, - DEFAULT_STORE_ID, - true - ); - await tx.wait(); - const balance = await GameSummary.balanceOf(playerAccount.address, hashIds(DEFAULT_STORE_ID, DEFAULT_GAME_ID)); - const balanceInt = Number(balance); - expect(balanceInt).to.equal(1); - }); - - it('As Admin must mint a game summary but not the same game summary twice', async function () { - const randomAchievementIds = [1234, 65441, 12312]; - const tx = await GameSummary.adminMintGameSummary( - playerAccount.address, - DEFAULT_GAME_ID, - randomAchievementIds.length, - DEFAULT_STORE_ID, - true - ); - await tx.wait(); - - await expect( - GameSummary.adminMintGameSummary( - playerAccount.address, - DEFAULT_GAME_ID, - randomAchievementIds.length, - DEFAULT_STORE_ID, - true - ) - // @ts-ignore-next-line - ).to.be.revertedWith('Token already exists'); - }); - - it('The pause functionality should works as expected', async function () { - const randomAchievementIds = [1234, 65441, 12312]; - const tx = await GameSummary.adminMintGameSummary( - playerAccount.address, - DEFAULT_GAME_ID, - randomAchievementIds.length, - DEFAULT_STORE_ID, - true - ); - await tx.wait(); - - await GameSummary.pause(); - - await expect( - GameSummary.adminMintGameSummary( - playerAccount.address, - DEFAULT_GAME_ID + 1, - randomAchievementIds.length, - DEFAULT_STORE_ID, - true - ) - // @ts-ignore-next-line - ).to.be.revertedWith('Pausable: paused'); - - await GameSummary.unpause(); - - await ( - await await GameSummary.createCommonGameSummary( - DEFAULT_STORE_ID, - DEFAULT_GAME_ID + 2, - 'GameTest1234', - 'test://on_chain_uri', - 'external_uri', - 500 - ) - ).wait(); - const tx2 = await GameSummary.adminMintGameSummary( - playerAccount.address, - DEFAULT_GAME_ID + 2, - randomAchievementIds.length, - DEFAULT_STORE_ID, - true - ); - await tx2.wait(); - const balance = await GameSummary.balanceOf( - playerAccount.address, - hashIds(DEFAULT_STORE_ID, DEFAULT_GAME_ID + 2) - ); - expect(Number(balance)).to.equal(1); - }); - - it('As admin must mint a batch of game summaries achievement for players', async function () { - const GAME_IDS = [208, 444, 555]; - const randomArrayAchievementsIds1 = [12345, 654421, 123132, 12312]; - const randomArrayAchievementsIds2 = [1231234, 6545641, 9999]; - const randomArrayAchievementsIds3 = [6661234, 33365441, 22212312]; - - for await (const gameID of GAME_IDS) { - await ( - await GameSummary.createCommonGameSummary( - DEFAULT_STORE_ID, - gameID, - 'GameTest1234', - 'test://on_chain_uri', - 'external_uri', - 9999999999 - ) - ).wait(); - } - - const tx = await GameSummary.adminBatchMintGameSummary( - [playerAccount.address, playerAccount.address, playerAccount.address], - GAME_IDS, - [ - randomArrayAchievementsIds1.length, - randomArrayAchievementsIds2.length, - randomArrayAchievementsIds3.length, - ], - [DEFAULT_STORE_ID, DEFAULT_STORE_ID, DEFAULT_STORE_ID], - [true, false, true] - ); - await tx.wait(); - - for (let i = 0; i < GAME_IDS.length; i++) { - const tokenId = hashIds(DEFAULT_STORE_ID, GAME_IDS[i]); - const balance = await GameSummary.balanceOf(playerAccount.address, tokenId); - const balanceInt = Number(balance); - expect(balanceInt).to.equal(1); - } - }); - - it('As game creator role I can create game summaries', async function () { - const summary = await GameSummary.getGameSummary(hashIds(DEFAULT_STORE_ID, DEFAULT_GAME_ID)); - expect(Number(summary.gameId)).to.equal(DEFAULT_GAME_ID); - expect(Number(summary.totalAchievements)).to.equal(500); - expect(summary.name).to.equal('GameTest1234'); - expect(summary.image).to.equal('test://on_chain_uri'); - expect(summary.externalURI).to.equal('external_uri'); - }); - - it('As an User I can get 1 GameSummary that the admin minted per game', async function () { - const tx = await GameSummary.adminMintGameSummary( - playerAccount.address, - DEFAULT_GAME_ID, - 20, - DEFAULT_STORE_ID, - true - ); - await tx.wait(); - const gameSummary = await GameSummary.connect(playerAccount).getGameSummary( - hashIds(DEFAULT_STORE_ID, DEFAULT_GAME_ID) - ); - expect(Number(gameSummary.gameId)).to.equal(DEFAULT_GAME_ID); - expect(Number(gameSummary.totalAchievements)).to.equal(500); - expect(gameSummary.name).to.equal('GameTest1234'); - expect(gameSummary.image).to.equal('test://on_chain_uri'); - expect(gameSummary.externalURI).to.equal('external_uri'); - }); - - it('As an User I can get 1 GameSummary that the admin minted per game and check the PlayerData', async function () { - const PLAYER_ACHIEVEMENTS_LENGTH = 20; - const TOTAL_ACHIEVEMENTS_IN_GAME = 500; - await ( - await GameSummary.createCommonGameSummary( - DEFAULT_STORE_ID + 1, - DEFAULT_GAME_ID, - 'GameTest1234', - 'test://on_chain_uri', - 'external_uri', - TOTAL_ACHIEVEMENTS_IN_GAME - ) - ).wait(); - const tx = await GameSummary.adminMintGameSummary( - playerAccount.address, - DEFAULT_GAME_ID, - PLAYER_ACHIEVEMENTS_LENGTH, - DEFAULT_STORE_ID + 1, - true - ); - await tx.wait(); - const gameSummary = await GameSummary.connect(playerAccount).getGameSummary( - hashIds(DEFAULT_STORE_ID + 1, DEFAULT_GAME_ID) - ); - expect(Number(gameSummary.gameId)).to.equal(DEFAULT_GAME_ID); - expect(Number(gameSummary.totalAchievements)).to.equal(TOTAL_ACHIEVEMENTS_IN_GAME); - const playerData = await GameSummary.connect(playerAccount).getPlayerGameData( - playerAccount.address, - hashIds(DEFAULT_STORE_ID + 1, DEFAULT_GAME_ID) - ); - expect(Number(playerData.achievementsMinted)).to.equal(PLAYER_ACHIEVEMENTS_LENGTH); - // missing achievements = total achievements - achievements minted - expect(Number(gameSummary.totalAchievements) - Number(playerData.achievementsMinted)).to.equal(480); - }); - - it('If the admin update one GameSummary, everyone will have this update', async function () { - const createCommonGameSummaryTrx = await GameSummary.createCommonGameSummary( - DEFAULT_STORE_ID + 1, - DEFAULT_GAME_ID, - 'GameTest1234', - 'test://on_chain_uri', - 'external_uri', - 500 - ); - await createCommonGameSummaryTrx.wait(); - const tx = await GameSummary.adminMintGameSummary( - playerAccount.address, - DEFAULT_GAME_ID, - 20, - DEFAULT_STORE_ID + 1, - true - ); - await tx.wait(); - const gameSummary = await GameSummary.connect(playerAccount).getGameSummary( - hashIds(DEFAULT_STORE_ID + 1, DEFAULT_GAME_ID) - ); - expect(gameSummary.image).to.equal('test://on_chain_uri'); - await GameSummary.updateCommonGameSummary( - hashIds(DEFAULT_STORE_ID + 1, DEFAULT_GAME_ID), - 'New_NAME', - 'test://on_chain_uri_2', - 'external_uri', - 577 - ); - const gameSummaryUpdated = await GameSummary.connect(playerAccount).getGameSummary( - hashIds(DEFAULT_STORE_ID + 1, DEFAULT_GAME_ID) - ); - expect(gameSummaryUpdated.image).to.equal('test://on_chain_uri_2'); - expect(gameSummaryUpdated.name).to.equal('New_NAME'); - expect(Number(gameSummaryUpdated.totalAchievements)).to.equal(500 + 77); - }); - - it('As an Admin the BaseURI functionality should works', async function () { - const tx = await GameSummary.setBaseUri('ipfs://some1234hash/folder/'); - await tx.wait(); - const baseURI = await GameSummary.baseUri(); - expect(baseURI).to.equal('ipfs://some1234hash/folder/'); - - const mintTx = await GameSummary.adminMintGameSummary( - playerAccount.address, - DEFAULT_GAME_ID, - 222, - DEFAULT_STORE_ID, - true - ); - await mintTx.wait(); - const uri = await GameSummary.uri(hashIds(DEFAULT_STORE_ID, DEFAULT_GAME_ID)); - expect(uri).to.equal(`ipfs://some1234hash/folder/${hashIds(DEFAULT_STORE_ID, DEFAULT_GAME_ID)}.json`); - }); - - it('As user I cant transfer/sell any achievement if is a SBT', async function () { - const tx = await GameSummary.adminMintGameSummary( - playerAccount.address, - DEFAULT_GAME_ID, - 20, - DEFAULT_STORE_ID, - true - ); - await tx.wait(); - await expect( - GameSummary.connect(playerAccount).safeTransferFrom( - playerAccount.address, - minterAccount.address, - hashIds(DEFAULT_STORE_ID, DEFAULT_GAME_ID), - 1, - ethers.toUtf8Bytes('') - ) - // @ts-ignore-next-line - ).to.be.revertedWith("You can't transfer this token"); - - // simulating a listing on any marketplace - await GameSummary.setApprovalForAll('0xa5409ec958c83c3f309868babaca7c86dcb077c1', true); - await expect( - GameSummary.connect(playerAccount).safeTransferFrom( - playerAccount.address, - '0xa5409ec958c83c3f309868babaca7c86dcb077c1', - hashIds(DEFAULT_STORE_ID, DEFAULT_GAME_ID), - 1, - ethers.toUtf8Bytes('') - ) - // @ts-ignore-next-line - ).to.be.revertedWith("You can't transfer this token"); - }); - - it('As user I can transfer/sell any achievement if is not a SBT', async function () { - const tx = await GameSummary.adminMintGameSummary( - playerAccount.address, - DEFAULT_GAME_ID, - 20, - DEFAULT_STORE_ID, - false - ); - await tx.wait(); - const transferTx = await GameSummary.connect(playerAccount).safeTransferFrom( - playerAccount.address, - minterAccount.address, - hashIds(DEFAULT_STORE_ID, DEFAULT_GAME_ID), - 1, - ethers.toUtf8Bytes('') - ); - await transferTx.wait(); - const balance = await GameSummary.balanceOf(minterAccount.address, hashIds(DEFAULT_STORE_ID, DEFAULT_GAME_ID)); - expect(Number(balance)).to.equal(1); - }); - - it('As user I cant transfer any SBT using the safeBatchTransferFrom', async function () { - const GAME_IDS = [DEFAULT_GAME_ID, DEFAULT_GAME_ID + 1, DEFAULT_GAME_ID + 2, DEFAULT_GAME_ID + 3]; - for await (const gameID of GAME_IDS) { - if (gameID != DEFAULT_GAME_ID) { - await ( - await GameSummary.createCommonGameSummary( - DEFAULT_STORE_ID, - gameID, - 'GameTest1234', - 'test://on_chain_uri', - 'external_uri', - 9999999999 - ) - ).wait(); - } - } - const TOKEN_ID_1 = hashIds(DEFAULT_STORE_ID, GAME_IDS[0]); - const TOKEN_ID_2 = hashIds(DEFAULT_STORE_ID, GAME_IDS[1]); - const TOKEN_ID_3 = hashIds(DEFAULT_STORE_ID, GAME_IDS[2]); - const TOKEN_ID_4 = hashIds(DEFAULT_STORE_ID, GAME_IDS[3]); - const sbt1Trx = await GameSummary.adminMintGameSummary( - playerAccount.address, - GAME_IDS[0], - 1, - DEFAULT_STORE_ID, - false - ); - const sbt2Trx = await GameSummary.adminMintGameSummary( - playerAccount.address, - GAME_IDS[1], - 1, - DEFAULT_STORE_ID, - true - ); - const sbt3Trx = await GameSummary.adminMintGameSummary( - playerAccount.address, - GAME_IDS[2], - 1, - DEFAULT_STORE_ID, - false - ); - const sbt4Trx = await GameSummary.adminMintGameSummary( - playerAccount.address, - GAME_IDS[3], - 1, - DEFAULT_STORE_ID, - false - ); - await sbt1Trx.wait(); - await sbt2Trx.wait(); - await sbt3Trx.wait(); - await sbt4Trx.wait(); - - // must revert because at least 1 token is an SBT - await expect( - GameSummary.connect(playerAccount).safeBatchTransferFrom( - playerAccount.address, - minterAccount.address, - [TOKEN_ID_1, TOKEN_ID_2, TOKEN_ID_3], - [1, 1, 1], - ethers.toUtf8Bytes('') - ) - // @ts-ignore-next-line - ).to.be.revertedWith("You can't transfer this token"); - - const balance = await GameSummary.balanceOf(minterAccount.address, `${TOKEN_ID_1}`); - const balance2 = await GameSummary.balanceOf(minterAccount.address, `${TOKEN_ID_2}`); - expect(Number(balance)).to.equal(0); - expect(Number(balance2)).to.equal(0); - - // But if the token is not an SBT, it should work - const transferTx = await GameSummary.connect(playerAccount).safeBatchTransferFrom( - playerAccount.address, - minterAccount.address, - [TOKEN_ID_3, TOKEN_ID_4], - [1, 1], - ethers.toUtf8Bytes('') - ); - await transferTx.wait(); - const balance3 = await GameSummary.balanceOf(minterAccount.address, `${TOKEN_ID_3}`); - const balance4 = await GameSummary.balanceOf(minterAccount.address, `${TOKEN_ID_4}`); - expect(Number(balance3)).to.equal(1); - expect(Number(balance4)).to.equal(1); - }); - - it('As a user I could burn any not SBT', async function () { - const TOKEN_ID_1 = hashIds(DEFAULT_STORE_ID, DEFAULT_GAME_ID); - const sbt1Trx = await GameSummary.adminMintGameSummary( - playerAccount.address, - DEFAULT_GAME_ID, - 1, - DEFAULT_STORE_ID, - false - ); - await sbt1Trx.wait(); - - const burnTx = await GameSummary.connect(playerAccount).burn(TOKEN_ID_1); - await burnTx.wait(); - - const balance = await GameSummary.balanceOf(playerAccount.address, TOKEN_ID_1); - expect(Number(balance)).to.equal(0); - }); - - it('As a user I can burn any SBT but the info of the player will be cleared', async function () { - const TOKEN_ID_1 = hashIds(DEFAULT_STORE_ID, DEFAULT_GAME_ID); - const sbt1Trx = await GameSummary.adminMintGameSummary( - playerAccount.address, - DEFAULT_GAME_ID, - 1, - DEFAULT_STORE_ID, - true - ); - await sbt1Trx.wait(); - await (await GameSummary.connect(playerAccount).burn(TOKEN_ID_1)).wait(); - - const gameSummaryBurned = await GameSummary.connect(playerAccount).getPlayerGameData( - playerAccount.address, - TOKEN_ID_1 - ); - await expect(gameSummaryBurned.achievementsMinted).to.equal(0); - await expect(gameSummaryBurned.tokenId).to.equal(0); - await expect(gameSummaryBurned.soulbounded).to.be.false; - }); - - it('should revert if a non-admin tries to set a signer', async function () { - await expect(GameSummary.connect(playerAccount).setSigner(minterAccount.address)) - // @ts-ignore-next-line - .to.be.revertedWith( - 'AccessControl: account 0x70997970c51812dc3a010c7d01b50e0d17dc79c8 is missing role 0x0000000000000000000000000000000000000000000000000000000000000000' - ); - }); - - it('should revert if a non-admin tries to remove a signer', async function () { - await expect(GameSummary.connect(playerAccount).removeSigner(minterAccount.address)) - // @ts-ignore-next-line - .to.be.revertedWith( - 'AccessControl: account 0x70997970c51812dc3a010c7d01b50e0d17dc79c8 is missing role 0x0000000000000000000000000000000000000000000000000000000000000000' - ); - }); - - it('should return true for supported interfaces', async function () { - expect(await GameSummary.supportsInterface('0x01ffc9a7')).to.be.true; // ERC165 - expect(await GameSummary.supportsInterface('0xd9b67a26')).to.be.true; // ERC1155 - }); - - it('should return false for unsupported interfaces', async function () { - expect(await GameSummary.supportsInterface('0xabcdef12')).to.be.false; - }); - - it('should return the correct uri for a given token ID', async function () { - expect(await GameSummary.uri(123)).to.equal('https://achievo.mypinata.cloud/ipfs/123.json'); - }); - - it('should allow batch burning of tokens', async function () { - const TOKEN_ID_1 = hashIds(DEFAULT_STORE_ID, DEFAULT_GAME_ID); - const TOKEN_ID_2 = hashIds(DEFAULT_STORE_ID, DEFAULT_GAME_ID + 1); - - await await GameSummary.createCommonGameSummary( - DEFAULT_STORE_ID, - DEFAULT_GAME_ID + 1, - 'testGame', - 'ipfs://hash', - 'https://cdn.comg/jpg', - 200 - ); - await GameSummary.adminMintGameSummary(playerAccount.address, DEFAULT_GAME_ID, 1, DEFAULT_STORE_ID, false); - await GameSummary.adminMintGameSummary(playerAccount.address, DEFAULT_GAME_ID + 1, 1, DEFAULT_STORE_ID, false); - - const burnTx = await GameSummary.connect(playerAccount).burnBatch([TOKEN_ID_1, TOKEN_ID_2]); - await burnTx.wait(); - - const balance1 = await GameSummary.balanceOf(playerAccount.address, TOKEN_ID_1); - const balance2 = await GameSummary.balanceOf(playerAccount.address, TOKEN_ID_2); - expect(Number(balance1)).to.equal(0); - expect(Number(balance2)).to.equal(0); - }); - - it('should revert if the signature is not valid', async function () { - await generateSignature({ walletAddress: playerAccount.address, signer: minterAccount }); - const whitelistTx = await GameSummary.setSigner(minterAccount.address); - await whitelistTx.wait(); - - // incorrect signer - const { signature, nonce } = await generateSignature({ - walletAddress: minterAccount.address, - signer: playerAccount, - }); - - await expect( - GameSummary.connect(playerAccount).mintGameSummaryWithSignature( - DEFAULT_GAME_ID, - 20, - DEFAULT_STORE_ID, - nonce, - signature - ) - // @ts-ignore-next-line - ).to.be.revertedWith('Invalid signature'); - }); - - it('as admin should update the qty of achievements for a player', async function () { - const tx = await GameSummary.adminMintGameSummary( - playerAccount.address, - DEFAULT_GAME_ID, - 20, - DEFAULT_STORE_ID, - true - ); - await tx.wait(); - - const updateTx = await GameSummary.adminUpdatePlayerAchievements( - playerAccount.address, - hashIds(DEFAULT_STORE_ID, DEFAULT_GAME_ID), - 23 - ); - await updateTx.wait(); - - const playerDataUpdated = await GameSummary.connect(playerAccount).getPlayerGameData( - playerAccount.address, - hashIds(DEFAULT_STORE_ID, DEFAULT_GAME_ID) - ); - expect(Number(playerDataUpdated.achievementsMinted)).to.equal(43); - }); - - it('as admin should update the qty of achievements for a player using the batch', async function () { - const GAME_IDS = [100, 234, 255]; - - for await (const gameID of GAME_IDS) { - await ( - await GameSummary.createCommonGameSummary( - DEFAULT_STORE_ID, - gameID, - 'GameTest1234', - 'test://on_chain_uri', - 'external_uri', - 9999999999 - ) - ).wait(); - } - - const TOKEN_IDS = GAME_IDS.map((gameId) => hashIds(DEFAULT_STORE_ID, gameId)); - - const tx = await GameSummary.adminBatchMintGameSummary( - [playerAccount.address, playerAccount.address, playerAccount.address], - GAME_IDS, - [20, 44, 55], - [DEFAULT_STORE_ID, DEFAULT_STORE_ID, DEFAULT_STORE_ID], - [true, true, true] - ); - await tx.wait(); - - const updateTx = await GameSummary.adminBatchPlayerUpdateAchievements( - [playerAccount.address, playerAccount.address, playerAccount.address], - TOKEN_IDS, - [1, 2, 3] - ); - await updateTx.wait(); - - const playerDataUpdated = await GameSummary.connect(playerAccount).getPlayerGameData( - playerAccount.address, - TOKEN_IDS[0] - ); - expect(Number(playerDataUpdated.achievementsMinted)).to.equal(21); - - const playerDataUpdated2 = await GameSummary.connect(playerAccount).getPlayerGameData( - playerAccount.address, - TOKEN_IDS[1] - ); - expect(Number(playerDataUpdated2.achievementsMinted)).to.equal(46); - - const playerDataUpdated3 = await GameSummary.connect(playerAccount).getPlayerGameData( - playerAccount.address, - TOKEN_IDS[2] - ); - expect(Number(playerDataUpdated3.achievementsMinted)).to.equal(58); - }); - - it('should update the qty of achievements for a player using the signature', async function () { - const { signature, nonce } = await generateSignature({ - walletAddress: playerAccount.address, - signer: minterAccount, - }); - const whitelistTx = await GameSummary.setSigner(minterAccount.address); - await whitelistTx.wait(); - - const tx = await GameSummary.connect(playerAccount).mintGameSummaryWithSignature( - DEFAULT_GAME_ID, - 20, - DEFAULT_STORE_ID, - nonce, - signature - ); - await tx.wait(); - - const { signature: signature2, nonce: nonce2 } = await generateSignature({ - walletAddress: playerAccount.address, - signer: minterAccount, - }); - - const updateTx = await GameSummary.connect(playerAccount).updatePlayerAchievementsWithSignature( - hashIds(DEFAULT_STORE_ID, DEFAULT_GAME_ID), - 23, - nonce2, - signature2 - ); - await updateTx.wait(); - - const playerDataUpdated = await GameSummary.connect(playerAccount).getPlayerGameData( - playerAccount.address, - hashIds(DEFAULT_STORE_ID, DEFAULT_GAME_ID) - ); - expect(Number(playerDataUpdated.achievementsMinted)).to.equal(43); - }); - - it('the batchUpdate using the signature should works as expected', async function () { - const GAME_IDS = [100, 234, 255]; - - for await (const gameID of GAME_IDS) { - await ( - await GameSummary.createCommonGameSummary( - DEFAULT_STORE_ID, - gameID, - 'GameTest1234', - 'test://on_chain_uri', - 'external_uri', - 9999999999 - ) - ).wait(); - } - - const TOKEN_IDS = GAME_IDS.map((gameId) => hashIds(DEFAULT_STORE_ID, gameId)); - - const { signature, nonce } = await generateSignature({ - walletAddress: playerAccount.address, - signer: minterAccount, - }); - const whitelistTx = await GameSummary.setSigner(minterAccount.address); - await whitelistTx.wait(); - - const tx = await GameSummary.connect(playerAccount).batchMintGameSummaryWithSignature( - GAME_IDS, - [20, 44, 55], - [DEFAULT_STORE_ID, DEFAULT_STORE_ID, DEFAULT_STORE_ID], - nonce, - signature - ); - await tx.wait(); - - const gameData = await GameSummary.connect(playerAccount).getPlayerGamesData(playerAccount.address, TOKEN_IDS); - expect(gameData.length).to.equal(3); - - const { signature: signature2, nonce: nonce2 } = await generateSignature({ - walletAddress: playerAccount.address, - signer: minterAccount, - }); - - const updateTx = await GameSummary.connect(playerAccount).batchPlayerUpdateAchievementsWithSignature( - TOKEN_IDS, - [2, 90, 20], - nonce2, - signature2 - ); - await updateTx.wait(); - - const playerDataUpdated = await GameSummary.connect(playerAccount).getPlayerGameData( - playerAccount.address, - TOKEN_IDS[0] - ); - expect(Number(playerDataUpdated.achievementsMinted)).to.equal(22); - - const playerDataUpdated2 = await GameSummary.connect(playerAccount).getPlayerGameData( - playerAccount.address, - TOKEN_IDS[1] - ); - expect(Number(playerDataUpdated2.achievementsMinted)).to.equal(134); - - const playerDataUpdated3 = await GameSummary.connect(playerAccount).getPlayerGameData( - playerAccount.address, - TOKEN_IDS[2] - ); - expect(Number(playerDataUpdated3.achievementsMinted)).to.equal(75); - }); - - it("As admin or user minting shouldn't have collisions between ids", async function () { - const GAME_ID = 110011; - const STORE_ID = 10101010; - await ( - await await GameSummary.createCommonGameSummary( - STORE_ID, - GAME_ID, - 'GameTest1234', - 'test://on_chain_uri', - 'external_uri', - 500 - ) - ).wait(); - const tokenID = hashIds(STORE_ID, GAME_ID); - const tx = await GameSummary.adminMintGameSummary(playerAccount.address, GAME_ID, 20, STORE_ID, true); - await tx.wait(); - const balanceBN = await GameSummary.connect(playerAccount).balanceOf(playerAccount.address, tokenID); - expect(Number(balanceBN)).to.equal(1); - }); -});