Skip to content

Commit

Permalink
more tests, added default admin to factory
Browse files Browse the repository at this point in the history
  • Loading branch information
MrDeadCe11 committed Aug 9, 2023
1 parent 1b15e49 commit ef91661
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 52 deletions.
1 change: 1 addition & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ solc = "0.8.15"
bytecode_hash = "none"
optimizer_runs = 1000000
fs_permissions = [{ access = "read-write", path = "./"}]
ignored_error_codes = ["license", "code-size", "unused-var"]
[profile.intense.fuzz]
runs = 10000
5 changes: 3 additions & 2 deletions src/factories/CharacterSheetsFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ contract CharacterSheetsFactory is OwnableUpgradeable {
function create(
address[] calldata dungeonMasters,
address dao,
address default_admin,
string calldata experienceBaseuri,
string calldata characterSheetsBaseUri
) external returns (address, address) {
Expand All @@ -51,9 +52,9 @@ contract CharacterSheetsFactory is OwnableUpgradeable {
address experienceClone = ClonesUpgradeable.cloneDeterministic(experienceAndItemsImplementation, 0);

bytes memory encodedCharacterSheetParameters =
abi.encode(dao, dungeonMasters, experienceClone, characterSheetsBaseUri);
abi.encode(dao, dungeonMasters, default_admin, experienceClone, characterSheetsBaseUri);
bytes memory encodedExperienceParameters =
abi.encode(dao, dungeonMasters, characterSheetsClone, hatsAddress, experienceBaseuri);
abi.encode(dao, default_admin, characterSheetsClone, hatsAddress, experienceBaseuri);

CharacterSheetsImplementation(characterSheetsClone).initialize(encodedCharacterSheetParameters);
ExperienceAndItemsImplementation(experienceClone).initialize(encodedExperienceParameters);
Expand Down
63 changes: 48 additions & 15 deletions src/implementations/CharacterSheetsImplementation.sol
Original file line number Diff line number Diff line change
Expand Up @@ -69,17 +69,17 @@ contract CharacterSheetsImplementation is Initializable, IMolochDAO, ERC721, ERC

address daoAddress;
address[] memory dungeonMasters;
address owner;
string memory baseUri;
address experienceImplementation;

(daoAddress, dungeonMasters, experienceImplementation, baseUri) =
abi.decode(_encodedParameters, (address, address[], address, string));
(daoAddress, dungeonMasters, owner, experienceImplementation, baseUri) =
abi.decode(_encodedParameters, (address, address[], address, address, string));

for (uint256 i = 0; i < dungeonMasters.length; i++) {
_grantRole(DUNGEON_MASTER, dungeonMasters[i]);
_grantRole(DEFAULT_ADMIN_ROLE, dungeonMasters[i]);
}

_grantRole(DEFAULT_ADMIN_ROLE, owner);
setBaseUri(baseUri);
_experience = ExperienceAndItemsImplementation(experienceImplementation);
_dao = IMolochDAO(daoAddress);
Expand Down Expand Up @@ -140,20 +140,53 @@ contract CharacterSheetsImplementation is Initializable, IMolochDAO, ERC721, ERC
players[playerId].classes.push(classTokenId);
emit classAdded(playerId, classTokenId);
}

function removeClassFromPlayer(uint256 playerId, uint256 classTokenId) external onlyExpContract returns(bool success){
/**
* removes a class from a character Sheet
* @param playerId the id of the character sheet to be modified
* @param classId the class Id to be removed
*/
function removeClassFromPlayer(uint256 playerId, uint256 classId) external onlyExpContract returns (bool success) {
uint256[] memory arr = players[playerId].classes;
for (uint256 i = 0; i < arr.length; i++) {
if (arr[i] == classTokenId) {
for (uint256 j = i; j < arr.length - 1; j++) {
arr[j] = arr[j + 1];
if (arr[i] == classId) {
for (uint256 j = i; j < arr.length; j++) {
if (j + 1 < arr.length) {
arr[j] = arr[j + 1];
} else if (j + 1 >= arr.length) {
arr[j] = 0;
}
}
arr[arr.length - 1] = 0;
players[playerId].classes = arr;
success = true;
players[playerId].classes.pop();

return success = true;
}
}
return success = false;
}
/**
* removes an itemtype from a character sheet inventory
* @param playerId the player to have the item type from their inventory
* @param itemId the itemId of the item to be removed
*/
function removeitemFromPlayer(uint256 playerId, uint256 itemId) external onlyExpContract returns (bool success) {
uint256[] memory arr = players[playerId].items;
for (uint256 i = 0; i < arr.length; i++) {
if (arr[i] == itemId) {
for (uint256 j = i; j < arr.length; j++) {
if (j + 1 < arr.length) {
arr[j] = arr[j + 1];
} else if (j + 1 >= arr.length) {
arr[j] = 0;
}
}
players[playerId].items = arr;
players[playerId].items.pop();

return success = true;
}
}
success = false;
return success = false;
}

function addItemToPlayer(uint256 playerId, uint256 itemTokenId) external onlyExpContract {
Expand All @@ -175,11 +208,11 @@ contract CharacterSheetsImplementation is Initializable, IMolochDAO, ERC721, ERC
revert("This is not the address of an npc");
}

function getClassIndex(uint256 playerId, uint256 classId)public view returns(uint256 indexOfClass){
function getClassIndex(uint256 playerId, uint256 classId) public view returns (uint256 indexOfClass) {
CharacterSheet memory sheet = players[playerId];
uint256 length = sheet.classes.length;
for(uint256 i =0; i<length; i++){
if(sheet.classes[i] == classId){
for (uint256 i = 0; i < length; i++) {
if (sheet.classes[i] == classId) {
indexOfClass = i;
}
}
Expand Down
19 changes: 10 additions & 9 deletions src/implementations/ExperienceAndItemsImplementation.sol
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ contract ExperienceAndItemsImplementation is ERC1155Holder, Initializable, ERC11

event newItemTypeCreated(uint256, uint256, string);
event newClassCreated(uint256, uint256, string, string);
event classAssigned(address, uint256);
event classAssigned(address, uint256, uint256);
event itemTransfered(address, uint256, uint256);
event itemUpdated(Item);

Expand Down Expand Up @@ -128,6 +128,7 @@ contract ExperienceAndItemsImplementation is ERC1155Holder, Initializable, ERC11
_mint(address(this), _tokenId, _newItem.supply, bytes(_newItem.cid));

_newItem.tokenId = _tokenId;
_newItem.itemId = _itemId;
items[_itemId] = _newItem;

emit newItemTypeCreated(_itemId, _tokenId, _newItem.name);
Expand Down Expand Up @@ -156,6 +157,7 @@ contract ExperienceAndItemsImplementation is ERC1155Holder, Initializable, ERC11
uint256 _tokenId = _tokenIdCounter.current();

_newClass.tokenId = _tokenId;
_newClass.classId = _classId;
classes[_classId] = _newClass;
_setURI(_tokenId, _newClass.cid);
emit newClassCreated(_tokenId, _classId, _newClass.name, _newClass.cid);
Expand All @@ -177,8 +179,8 @@ contract ExperienceAndItemsImplementation is ERC1155Holder, Initializable, ERC11
string memory temp = _name;
for (uint256 i = 0; i <= totalItemTypes; i++) {
if (keccak256(abi.encode(items[i].name)) == keccak256(abi.encode(temp))) {
itemId = i;
tokenId = items[i].tokenId;
itemId = items[i].itemId;
return (tokenId, itemId);
}
}
Expand All @@ -198,7 +200,7 @@ contract ExperienceAndItemsImplementation is ERC1155Holder, Initializable, ERC11
if (keccak256(abi.encode(classes[i].name)) == keccak256(abi.encode(temp))) {
//classid, tokenId;
tokenId = classes[i].tokenId;
classId = i;
classId = classes[i].classId;
return (tokenId, classId);
}
}
Expand Down Expand Up @@ -260,24 +262,23 @@ contract ExperienceAndItemsImplementation is ERC1155Holder, Initializable, ERC11
CharacterSheet memory player =
characterSheets.getCharacterSheetByPlayerId(playerId);
Class memory newClass = classes[classId];
console2.log("ASSIGN CLASS", newClass.tokenId, newClass.name);

require(player.memberAddress != address(0x0), "This member is not a player character");
require(newClass.tokenId > 0, "This class does not exist.");
require(balanceOf(player.ERC6551TokenAddress, newClass.tokenId) == 0, "Can only assign a class once.");

_mint(player.ERC6551TokenAddress, newClass.tokenId, 1, bytes(newClass.cid));

characterSheets.addClassToPlayer(playerId, newClass.tokenId);
characterSheets.addClassToPlayer(playerId, newClass.classId);

classes[classId].supply++;

emit classAssigned(player.ERC6551TokenAddress, classId);
emit classAssigned(player.ERC6551TokenAddress, classId, newClass.tokenId);
}


function assignClasses(uint256 playerId, uint256[] calldata _classIds) external onlyDungeonMaster {
for (uint256 i = 0; i < _classIds.length; i++) {
console2.log("CLASS IDS: ", _classIds[i], i);
assignClass(playerId, _classIds[i]);
}
}
Expand All @@ -292,11 +293,11 @@ contract ExperienceAndItemsImplementation is ERC1155Holder, Initializable, ERC11
CharacterSheet memory sheet = characterSheets.getCharacterSheetByPlayerId(playerId);
uint256 tokenId = classes[classId].tokenId;
if(characterSheets.hasRole(DUNGEON_MASTER, msg.sender)){
require(characterSheets.removeClassFromPlayer(playerId, tokenId), "Player does not have that class");
require(characterSheets.removeClassFromPlayer(playerId, classId), "Player does not have that class");
_burn(sheet.ERC6551TokenAddress, tokenId, 1);
} else {
require(sheet.memberAddress == msg.sender, "Must be the player to remove a class");
require(characterSheets.removeClassFromPlayer(playerId, tokenId), "You do not have that class");
require(characterSheets.removeClassFromPlayer(playerId, classId), "You do not have that class");
_burn(sheet.ERC6551TokenAddress, tokenId, 1);
}
success = true;
Expand Down
47 changes: 28 additions & 19 deletions test/ExperienceAndItems.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@ contract ExperienceAndItemsTest is Test, SetUp {
function testCreateClass() public {
vm.prank(admin);
(uint256 _tokenId, uint256 _classId) = experience.createClassType(createNewClass("Ballerina"));
(uint256 tokenId, string memory name, uint256 supply, string memory cid) = experience.classes(_classId);
(uint256 tokenId, uint256 classId, string memory name, uint256 supply, string memory cid) = experience.classes(_classId);

assertEq(experience.totalClasses(), 2);
assertEq(tokenId, 3);
assertEq(_tokenId, 3);
assertEq(_classId, 2);
assertEq(classId, 2);
assertEq(keccak256(abi.encode(name)), keccak256(abi.encode("Ballerina")));
assertEq(supply, 0);
assertEq(keccak256(abi.encode(cid)), keccak256(abi.encode("test_class_cid/")));
Expand All @@ -37,7 +38,7 @@ contract ExperienceAndItemsTest is Test, SetUp {

assertEq(experience.balanceOf(player.ERC6551TokenAddress, tokenId), 1);
assertEq(player.classes.length, 1);
assertEq(player.classes[0], tokenId);
assertEq(player.classes[0], classId);

//add second class
vm.prank(admin);
Expand All @@ -47,7 +48,7 @@ contract ExperienceAndItemsTest is Test, SetUp {

assertEq(experience.balanceOf(secondPlayer.ERC6551TokenAddress, 2), 1, "does not own second class");
assertEq(secondPlayer.classes.length, 2, "not enough classes");
assertEq(secondPlayer.classes[1], 2, 'second class not in player classes array');
assertEq(secondPlayer.classes[1], 1, 'second class not in player classes array');
}

function testAssignClasses()public {
Expand All @@ -58,33 +59,40 @@ contract ExperienceAndItemsTest is Test, SetUp {
Class[] memory allClasses = experience.getAllClasses();

uint256[] memory classes = new uint256[](2);
classes[0] = 1;
classes[1] = 2;
classes[0] = allClasses[0].classId;
classes[1] = allClasses[1].classId;
experience.assignClasses(playerId, classes);
vm.stopPrank();
CharacterSheet memory player = characterSheets.getCharacterSheetByPlayerId(playerId);
assertEq(player.classes.length, 2, "not enough classes assigned");
assertEq(player.classes[0], classId);
assertEq(player.classes[1], 1);
assertEq(player.classes[0], allClasses[0].classId, "wrong classId");
assertEq(player.classes[1], allClasses[1].classId, "wrong classid 2");
}

// function testRemoveClass() public {
// uint256 playerId = characterSheets.memberAddressToTokenId(player1);
function testRevokeClass() public {
uint256 playerId = characterSheets.memberAddressToTokenId(player1);

// vm.startPrank(admin);
// (uint256 tokenId, uint256 classId) = experience.createClassType(createNewClass("Ballerina"));
vm.startPrank(admin);
(uint256 tokenId, uint256 classId) = experience.createClassType(createNewClass("Ballerina"));

// experience.assignClass(playerId, classId);
// vm.stopPrank();
Class[] memory allClasses = experience.getAllClasses();

// vm.prank(player1);
// experience.revokeClass(playerId, classId);
uint256[] memory classes = new uint256[](2);
classes[0] = allClasses[0].classId;
classes[1] = allClasses[1].classId;

// CharacterSheet memory sheet = characterSheets.getCharacterSheetByPlayerId(playerId);
experience.assignClasses(playerId, classes);
vm.stopPrank();

vm.prank(player1);
experience.revokeClass(playerId, allClasses[0].classId);

CharacterSheet memory sheet = characterSheets.getCharacterSheetByPlayerId(playerId);

// assertEq(experience.balanceOf(sheet.ERC6551TokenAddress, tokenId), 0);
// assertEq(sheet.classes.length, 0);
// }
assertEq(experience.balanceOf(sheet.ERC6551TokenAddress, tokenId), 1, "Incorrect class balance");
assertEq(sheet.classes.length, 1, "classes array wrong length.");
assertEq(sheet.classes[0], allClasses[1].classId, "wrong remaining id");
}

function testCreateItemType() public {
Item memory newItem = createNewItem("Pirate_Hat", false, bytes32(0));
Expand All @@ -93,6 +101,7 @@ contract ExperienceAndItemsTest is Test, SetUp {

(
uint256 tokenId,
uint256 itemId,
string memory name,
uint256 supply,
uint256 supplied,
Expand Down
16 changes: 9 additions & 7 deletions test/helpers/SetUp.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {ERC6551Registry} from "../../src/mocks/ERC6551Registry.sol";
import {SimpleERC6551Account} from "../../src/mocks/ERC6551Implementation.sol";

contract SetUp is Test {

ExperienceAndItemsImplementation experienceAndItemsImplementation;
ExperienceAndItemsImplementation experience;
CharacterSheetsFactory characterSheetsFactory;
Expand Down Expand Up @@ -45,7 +46,7 @@ contract SetUp is Test {
function setUp() public {


Item memory newItem = createNewItem("test_item", false, bytes32(0));

vm.startPrank(admin);

dao = new Moloch();
Expand All @@ -70,8 +71,10 @@ contract SetUp is Test {
characterSheetsFactory.updateHats(address(hats));
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/');
(characterSheetsAddress, experienceAddress) = characterSheetsFactory.create(dungeonMasters, address(dao), admin,'test_base_uri_experience/', 'test_base_uri_character_sheets/');
characterSheets = CharacterSheetsImplementation(characterSheetsAddress);
experience = ExperienceAndItemsImplementation(experienceAddress);

characterSheets.setERC6551Registry(address(erc6551Registry));
characterSheets.setERC6551Implementation(address(erc6551Implementation));

Expand All @@ -84,18 +87,18 @@ contract SetUp is Test {

erc6551Registry = new ERC6551Registry();
erc6551Implementation = new SimpleERC6551Account();
experience = ExperienceAndItemsImplementation(experienceAddress);
experience.createItemType(newItem);

experience.createItemType(createNewItem("test_item", false, bytes32(0)));
experience.createClassType(createNewClass('test_class'));
vm.stopPrank();
}

function createNewItem(string memory _name, bool _soulbound, bytes32 _claimable)public pure returns(Item memory){
return Item({tokenId: 0, name: _name, supply: 10**18, supplied: 0, experienceCost: 100, hatId: 0, soulbound: _soulbound, claimable: _claimable, cid: 'test_item_cid/'});
return Item({tokenId: 0, itemId: 0, name: _name, supply: 10**18, supplied: 0, experienceCost: 100, hatId: 0, soulbound: _soulbound, claimable: _claimable, cid: 'test_item_cid/'});
}

function createNewClass(string memory _name)public pure returns(Class memory){
return Class({tokenId: 0, name: _name, supply: 0, cid: 'test_class_cid/'});
return Class({tokenId: 0, classId: 0, name: _name, supply: 0, cid: 'test_class_cid/'});
}

function dropExp(address player, uint256 amount)public{
Expand All @@ -112,7 +115,6 @@ contract SetUp is Test {
function generateMerkleRootAndProof(uint256[] memory itemIds, address[] memory claimers, uint256[] memory amounts, uint256 indexOfProof) public view returns(bytes32[] memory proof, bytes32 root) {
bytes32[] memory leaves = new bytes32[](itemIds.length);
for(uint256 i =0; i< itemIds.length; i++){

leaves[i] = keccak256(bytes.concat(keccak256(abi.encodePacked(itemIds[i], claimers[i], amounts[i]))));
}
proof = merkle.getProof(leaves, indexOfProof);
Expand Down

0 comments on commit ef91661

Please sign in to comment.