diff --git a/src/factories/CharacterSheetsFactory.sol b/src/factories/CharacterSheetsFactory.sol index 4417b5b..2e6fd1f 100644 --- a/src/factories/CharacterSheetsFactory.sol +++ b/src/factories/CharacterSheetsFactory.sol @@ -37,7 +37,7 @@ contract CharacterSheetsFactory is OwnableUpgradeable { } function create( - address dungeonMaster, + address[] calldata dungeonMasters, address dao, string calldata experienceBaseuri, string calldata characterSheetsBaseUri @@ -51,9 +51,9 @@ contract CharacterSheetsFactory is OwnableUpgradeable { address experienceClone = ClonesUpgradeable.cloneDeterministic(experienceAndItemsImplementation, 0); bytes memory encodedCharacterSheetParameters = - abi.encode(dao, dungeonMaster, experienceClone, characterSheetsBaseUri); + abi.encode(dao, dungeonMasters, experienceClone, characterSheetsBaseUri); bytes memory encodedExperienceParameters = - abi.encode(dao, dungeonMaster, characterSheetsClone, hatsAddress, experienceBaseuri); + abi.encode(dao, dungeonMasters, characterSheetsClone, hatsAddress, experienceBaseuri); CharacterSheetsImplementation(characterSheetsClone).initialize(encodedCharacterSheetParameters); ExperienceAndItemsImplementation(experienceClone).initialize(encodedExperienceParameters); diff --git a/src/implementations/CharacterSheetsImplementation.sol b/src/implementations/CharacterSheetsImplementation.sol index ad9fae5..0b33a47 100644 --- a/src/implementations/CharacterSheetsImplementation.sol +++ b/src/implementations/CharacterSheetsImplementation.sol @@ -18,6 +18,8 @@ import "forge-std/console2.sol"; string name; address ERC6551TokenAddress; address memberAddress; + uint256[] classes; + uint256[] items; } contract CharacterSheetsImplementation is @@ -59,27 +61,45 @@ contract CharacterSheetsImplementation is constructor() ERC721("CharacterSheet", "CHAS") { _disableInitializers(); } - + /** + * + * @param _encodedParameters encoded parameters must include: + * @dev address daoAddress the address of the dao who's member list will be allowed to become players and who will be able to interact with this contract + * @dev address[] dungeonMasters an array addresses of the person/persons who are authorized to issue player cards, classes, and items. + * @dev string baseURI the default uri of the player card images, arbitrary a different uri can be set when the character sheet is minted. + * @dev address experienceImplementation this is the address of the ERC1155 experience contract associated with this contract. this is assigned at contract creation. + */ function initialize(bytes calldata _encodedParameters) public initializer { _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); _grantRole(DUNGEON_MASTER, msg.sender); address daoAddress; - address dungeonMaster; + address[] memory dungeonMasters; string memory baseUri; address experienceImplementation; - (daoAddress, dungeonMaster, experienceImplementation, baseUri) = - abi.decode(_encodedParameters, (address, address, address, string)); + (daoAddress, dungeonMasters, experienceImplementation, baseUri) = + abi.decode(_encodedParameters, (address, address[], address, string)); - _grantRole(DEFAULT_ADMIN_ROLE, dungeonMaster); - _grantRole(DUNGEON_MASTER, dungeonMaster); + + for(uint256 i = 0; i 0) { _setTokenURI(tokenId, _tokenURI); + } else { _setTokenURI(tokenId, _baseTokenURI); } //calculate ERC6551 account address @@ -196,4 +217,48 @@ contract CharacterSheetsImplementation is { return super.supportsInterface(interfaceId); } + + /// transfer overrides since these tokens should be soulbound or only transferable by the dungeonMaster + + /** + * @dev See {IERC721-approve}. + */ + function approve(address to, uint256 tokenId) public virtual override(ERC721, IERC721){ + revert("This token can only be transfered by the dungeon master"); + } + + /** + * @dev See {IERC721-setApprovalForAll}. + */ + function setApprovalForAll(address operator, bool approved) public virtual override(ERC721, IERC721){ + revert("This token can only be transfered by the dungeon master"); + } + + /** + * @dev See {IERC721-transferFrom}. + */ + function transferFrom(address from, address to, uint256 tokenId) public virtual override(ERC721, IERC721) onlyRole(DUNGEON_MASTER){ + _transfer(from, to, tokenId); + } + + /** + * @dev See {IERC721-safeTransferFrom}. + */ + function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override(ERC721, IERC721) onlyRole(DUNGEON_MASTER) { + safeTransferFrom(from, to, tokenId, ""); + } + + /** + * @dev See {IERC721-safeTransferFrom}. + */ + function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public virtual override(ERC721, IERC721) onlyRole(DUNGEON_MASTER) { + _safeTransfer(from, to, tokenId, data); + } + + function renounceSheet(uint256 _playerId)public returns(bool success){ + address tokenOwner = ownerOf(_playerId); + require(msg.sender == tokenOwner, "You cannot renounce a token you don't own"); + _burn(_playerId); + success = true; + } } diff --git a/src/implementations/ExperienceAndItemsImplementation.sol b/src/implementations/ExperienceAndItemsImplementation.sol index a86fef7..6c21b54 100644 --- a/src/implementations/ExperienceAndItemsImplementation.sol +++ b/src/implementations/ExperienceAndItemsImplementation.sol @@ -37,8 +37,7 @@ struct Class { contract ExperienceAndItemsImplementation is ERC1155Holder, Initializable, - ERC1155, - IMolochDAO + ERC1155 { using Strings for uint256; using Counters for Counters.Counter; @@ -165,6 +164,13 @@ contract ExperienceAndItemsImplementation is return (_tokenId, _itemId); } + /** + * + * @param _newClass A class struct with all the class details filled out + * @return tokenId the ERC1155 token id + * @return classId the location of the class in the classes mapping + */ + function createClassType( Class memory _newClass ) public onlyDungeonMaster returns (uint256 tokenId, uint256 classId) { @@ -182,7 +188,12 @@ contract ExperienceAndItemsImplementation is return (_tokenId, _classId); } - //reverts if no item found + /** + * + * @param _name a string with the name of the item. is case sensetive so it is preffered that all names are lowercase alphanumeric names enforced in the frontend. + * @return tokenId the ERC1155 token id. + * @return itemId the location of the item in the items mapping; + */ function findItemByName( string memory _name ) public view returns (uint256 tokenId, uint256 itemId) { @@ -200,7 +211,12 @@ contract ExperienceAndItemsImplementation is revert("No item found."); } - //reverts if no class found; + /** + * + * @param _name the name of the class. is case sensetive. + * @return tokenId the ERC1155 token id. + * @return classId storage location of the class in the classes mapping + */ function findClassByName( string calldata _name ) public view returns (uint256 tokenId, uint256 classId) { @@ -365,7 +381,7 @@ contract ExperienceAndItemsImplementation is } } /** - * + * this is to be claimed from the ERC6551 wallet of the player sheet. * @param itemIds an array of item ids * @param amounts an array of amounts to claim, must match the order of item ids * @param proofs an array of proofs allowing this address to claim the item, must be in same order as item ids and amounts @@ -411,12 +427,6 @@ contract ExperienceAndItemsImplementation is super.setApprovalForAll(operator, approved); } - function members( - address memberAddress - ) external override returns (Member memory member) { - return molochDao.members(memberAddress); - } - function supportsInterface( bytes4 interfaceId ) public view override(ERC1155Receiver, ERC1155) returns (bool) { @@ -469,4 +479,6 @@ contract ExperienceAndItemsImplementation is _baseURI = baseURI; } + //#TODO OVER RIDE TRANSFER FUNCTIONS etc for correct soulbindind of tokens. + } diff --git a/test/ExperienceAndItems.t.sol b/test/ExperienceAndItems.t.sol index 6cb133e..597b462 100644 --- a/test/ExperienceAndItems.t.sol +++ b/test/ExperienceAndItems.t.sol @@ -35,7 +35,7 @@ contract ExperienceAndItemsTest is Test, SetUp { } function testCreateItemType() public { - Item memory newItem = createNewItem("Pirate", false, bytes32(0)); + Item memory newItem = createNewItem("Pirate_Hat", false, bytes32(0)); vm.prank(admin); (uint256 _tokenId, uint256 _itemId) = experience.createItemType(newItem); @@ -52,7 +52,7 @@ contract ExperienceAndItemsTest is Test, SetUp { ) = experience.items(_itemId); assert(experience.totalItemTypes() == 2); - assert(keccak256(abi.encode(name)) == keccak256(abi.encode("Pirate"))); + assert(keccak256(abi.encode(name)) == keccak256(abi.encode("Pirate_Hat"))); assert(tokenId == 3); assert(_tokenId == 3); assert(supply == 10**18); @@ -60,7 +60,9 @@ contract ExperienceAndItemsTest is Test, SetUp { assert(experinceCost == 100); assert(soulbound == false); assert(claimable == bytes32(0)); - assert(keccak256(abi.encode(cid)) == keccak256(abi.encode('test_item_cid/'))); + assertEq(keccak256(abi.encode(cid)), keccak256(abi.encode('test_item_cid/'))); + assertEq(keccak256(abi.encode(experience.uri(tokenId))), keccak256(abi.encode('test_base_uri_experience/test_item_cid/')), "uris not right"); + } function testDropLoot()public{ diff --git a/test/helpers/SetUp.sol b/test/helpers/SetUp.sol index 23d4117..547bc7d 100644 --- a/test/helpers/SetUp.sol +++ b/test/helpers/SetUp.sol @@ -66,7 +66,9 @@ contract SetUp is Test { characterSheetsFactory.updateCharacterSheetsImplementation(address(characterSheetsImplementation)); characterSheetsFactory.updateExperienceAndItemsImplementation(address(experienceAndItemsImplementation)); characterSheetsFactory.updateHats(address(hats)); - (characterSheetsAddress, experienceAddress) = characterSheetsFactory.create(admin, address(dao), 'test_base_uri_experience/', 'test_base_uri_character_sheets/'); + address[] memory dungeonMasters = new address[](1); + dungeonMasters[0] = admin; + (characterSheetsAddress, experienceAddress) = characterSheetsFactory.create(dungeonMasters, address(dao), 'test_base_uri_experience/', 'test_base_uri_character_sheets/'); characterSheets = CharacterSheetsImplementation(characterSheetsAddress); characterSheets.setERC6551Registry(address(erc6551Registry)); characterSheets.setERC6551Implementation(address(erc6551Implementation));