From 456ec8e866a62da72f29dd40f328c7c84ea617b8 Mon Sep 17 00:00:00 2001 From: Neeraj Kashyap Date: Mon, 9 Jan 2023 20:36:35 -0800 Subject: [PATCH] Added test for equipping on top of an already equipped slot --- contracts/inventory/Inventory.sol | 27 ++-- game7ctl/game7ctl/test_inventory.py | 190 ++++++++++++++++++++++++++++ 2 files changed, 201 insertions(+), 16 deletions(-) diff --git a/contracts/inventory/Inventory.sol b/contracts/inventory/Inventory.sol index d082317..46a2ee6 100644 --- a/contracts/inventory/Inventory.sol +++ b/contracts/inventory/Inventory.sol @@ -385,22 +385,18 @@ contract InventoryFacet is "InventoryFacet.equip: Message sender is not owner of subject token" ); - LibInventory.EquippedItem memory existingItem = istore.EquippedItems[ - istore.SubjectERC721Address - ][subjectTokenId][slot]; - - // TODO(zomglings): To set things up, we will only test equipping workflow. To make it - // so that tokens cannot be unequipped, we require that the existingItem.ItemType be zero. - // We will turn this off when we add support for unequipping items from unequippable slots - // and when we want to add support for reupping items of the same already equipped type into - // a slot. - require( - existingItem.ItemType == 0, - "InventoryFacet.equip: This is a temporary restriction that no item can already be equipped in the given slot" - ); + // TODO(zomglings): Although this does the job, it is not gas-efficient if the caller is + // increasing the amount of an existing token in the given slot. To increase gas-efficiency, + // we could add more complex logic here to handle that situation by only equipping the difference + // between the existing amount of the token and the target amount. + if ( + istore + .EquippedItems[istore.SubjectERC721Address][subjectTokenId][slot] + .ItemType != 0 + ) { + _unequip(subjectTokenId, slot, true, 0); + } - // TODO(zomglings): When we support reupping items, we will need to modify the amount in the check - // below to amount + existingItem.amount. require( // Note the if statement when accessing the itemPoolId key in the SlotEligibleItems mapping. // That field is only relevant for ERC1155 tokens. For ERC20 and ERC721 tokens, the capacity @@ -413,7 +409,6 @@ contract InventoryFacet is "InventoryFacet.equip: You can not equip those many instances of that item into the given slot" ); - // TODO(zomglings): Add case here when we need to support unequipping. if (itemType == LibInventory.ERC20_ITEM_TYPE) { IERC20 erc20Contract = IERC20(itemAddress); bool erc20TransferSuccess = erc20Contract.transferFrom( diff --git a/game7ctl/game7ctl/test_inventory.py b/game7ctl/game7ctl/test_inventory.py index 9a77985..65ce318 100644 --- a/game7ctl/game7ctl/test_inventory.py +++ b/game7ctl/game7ctl/test_inventory.py @@ -1472,3 +1472,193 @@ def test_player_can_unequip_all_erc1155_items_in_slot_on_their_subject_tokens(se item_unequipped_events[0]["args"]["unequippedBy"], self.player.address, ) + + def test_player_can_equip_an_item_and_then_replace_it_onto_their_subject_tokens_20_then_1155( + self, + ): + # Mint tokens to player and set approvals + subject_token_id = self.nft.total_supply() + self.nft.mint(self.player.address, subject_token_id, {"from": self.owner}) + + self.payment_token.mint(self.player.address, 1000, {"from": self.owner}) + self.payment_token.approve( + self.inventory.address, MAX_UINT, {"from": self.player} + ) + + self.terminus.create_pool_v1(MAX_UINT, True, True, self.owner_tx_config) + item_pool_id = self.terminus.total_pools() + self.terminus.mint( + self.player.address, item_pool_id, 100, "", self.owner_tx_config + ) + self.terminus.set_approval_for_all( + self.inventory.address, True, {"from": self.player} + ) + + # Create inventory slot + unequippable = True + self.inventory.create_slot(unequippable, {"from": self.admin}) + slot = self.inventory.num_slots() + + # Set ERC20 token as equippable in slot with max amount of 10 + self.inventory.mark_item_as_equippable_in_slot( + slot, 20, self.payment_token.address, 0, 10, {"from": self.admin} + ) + + # Set ERC1155 token as equippable in slot with max amount of 10 + self.inventory.mark_item_as_equippable_in_slot( + slot, 1155, self.terminus.address, item_pool_id, 10, {"from": self.admin} + ) + + player_erc20_balance_0 = self.payment_token.balance_of(self.player.address) + inventory_erc20_balance_0 = self.payment_token.balance_of( + self.inventory.address + ) + + tx_receipt_0 = self.inventory.equip( + subject_token_id, + slot, + 20, + self.payment_token.address, + 0, + 2, + {"from": self.player}, + ) + + player_erc20_balance_1 = self.payment_token.balance_of(self.player.address) + inventory_erc20_balance_1 = self.payment_token.balance_of( + self.inventory.address + ) + + self.assertEqual(player_erc20_balance_1, player_erc20_balance_0 - 2) + self.assertEqual(inventory_erc20_balance_1, inventory_erc20_balance_0 + 2) + + equipped_item = self.inventory.equipped(subject_token_id, slot) + self.assertEqual(equipped_item, (20, self.payment_token.address, 0, 2)) + + player_erc1155_balance_1 = self.terminus.balance_of( + self.player.address, item_pool_id + ) + inventory_erc1155_balance_1 = self.terminus.balance_of( + self.inventory.address, item_pool_id + ) + + tx_receipt_1 = self.inventory.equip( + subject_token_id, + slot, + 1155, + self.terminus.address, + item_pool_id, + 9, + {"from": self.player}, + ) + + player_erc20_balance_2 = self.payment_token.balance_of(self.player.address) + inventory_erc20_balance_2 = self.payment_token.balance_of( + self.inventory.address + ) + + self.assertEqual(player_erc20_balance_2, player_erc20_balance_0) + self.assertEqual(inventory_erc20_balance_2, inventory_erc20_balance_0) + + equipped_item = self.inventory.equipped(subject_token_id, slot) + self.assertEqual(equipped_item, (1155, self.terminus.address, item_pool_id, 9)) + + player_erc1155_balance_2 = self.terminus.balance_of( + self.player.address, item_pool_id + ) + inventory_erc1155_balance_2 = self.terminus.balance_of( + self.inventory.address, item_pool_id + ) + + self.assertEqual(player_erc1155_balance_2, player_erc1155_balance_1 - 9) + self.assertEqual(inventory_erc1155_balance_2, inventory_erc1155_balance_1 + 9) + + item_equipped_events = _fetch_events_chunk( + web3_client, + inventory_events.ITEM_EQUIPPED_ABI, + from_block=tx_receipt_0.block_number, + to_block=tx_receipt_1.block_number, + ) + self.assertEqual(len(item_equipped_events), 2) + + self.assertEqual( + item_equipped_events[0]["args"]["subjectTokenId"], subject_token_id + ) + self.assertEqual(item_equipped_events[0]["args"]["slot"], slot) + self.assertEqual( + item_equipped_events[0]["args"]["itemType"], + 20, + ) + self.assertEqual( + item_equipped_events[0]["args"]["itemAddress"], + self.payment_token.address, + ) + self.assertEqual( + item_equipped_events[0]["args"]["itemTokenId"], + 0, + ) + self.assertEqual( + item_equipped_events[0]["args"]["amount"], + 2, + ) + self.assertEqual( + item_equipped_events[0]["args"]["equippedBy"], + self.player.address, + ) + + self.assertEqual( + item_equipped_events[1]["args"]["subjectTokenId"], subject_token_id + ) + self.assertEqual(item_equipped_events[1]["args"]["slot"], slot) + self.assertEqual( + item_equipped_events[1]["args"]["itemType"], + 1155, + ) + self.assertEqual( + item_equipped_events[1]["args"]["itemAddress"], + self.terminus.address, + ) + self.assertEqual( + item_equipped_events[1]["args"]["itemTokenId"], + item_pool_id, + ) + self.assertEqual( + item_equipped_events[1]["args"]["amount"], + 9, + ) + self.assertEqual( + item_equipped_events[1]["args"]["equippedBy"], + self.player.address, + ) + + item_unequipped_events = _fetch_events_chunk( + web3_client, + inventory_events.ITEM_UNEQUIPPED_ABI, + from_block=tx_receipt_1.block_number, + to_block=tx_receipt_1.block_number, + ) + self.assertEqual(len(item_unequipped_events), 1) + self.assertEqual( + item_unequipped_events[0]["args"]["subjectTokenId"], subject_token_id + ) + self.assertEqual(item_unequipped_events[0]["args"]["slot"], slot) + self.assertEqual( + item_unequipped_events[0]["args"]["itemType"], + 20, + ) + self.assertEqual( + item_unequipped_events[0]["args"]["itemAddress"], + self.payment_token.address, + ) + self.assertEqual( + item_unequipped_events[0]["args"]["itemTokenId"], + 0, + ) + self.assertEqual( + item_unequipped_events[0]["args"]["amount"], + 2, + ) + self.assertEqual( + item_unequipped_events[0]["args"]["unequippedBy"], + self.player.address, + )