From fd30e6ae271681cd809edf3f23070cca4a5d9525 Mon Sep 17 00:00:00 2001 From: pgendreau Date: Sat, 7 Dec 2024 15:46:40 -0500 Subject: [PATCH 1/8] AGIP135: add batch functions for equiping wearables --- contracts/Aavegotchi/facets/ItemsFacet.sol | 38 ++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/contracts/Aavegotchi/facets/ItemsFacet.sol b/contracts/Aavegotchi/facets/ItemsFacet.sol index 111b1da2..7e6e6e2a 100644 --- a/contracts/Aavegotchi/facets/ItemsFacet.sol +++ b/contracts/Aavegotchi/facets/ItemsFacet.sol @@ -183,7 +183,7 @@ contract ItemsFacet is Modifiers { function equipWearables( uint256 _tokenId, uint16[EQUIPPED_WEARABLE_SLOTS] calldata _wearablesToEquip - ) onlyAavegotchiOwner(_tokenId) onlyUnlocked(_tokenId) external { + ) onlyAavegotchiOwner(_tokenId) onlyUnlocked(_tokenId) public { uint256[EQUIPPED_WEARABLE_SLOTS] memory _depositIds; _equipWearables(_tokenId, _wearablesToEquip, _depositIds); } @@ -199,10 +199,44 @@ contract ItemsFacet is Modifiers { uint256 _tokenId, uint16[EQUIPPED_WEARABLE_SLOTS] calldata _wearablesToEquip, uint256[EQUIPPED_WEARABLE_SLOTS] calldata _depositIds - ) onlyAavegotchiOwner(_tokenId) onlyUnlocked(_tokenId) external { + ) onlyAavegotchiOwner(_tokenId) onlyUnlocked(_tokenId) public { _equipWearables(_tokenId, _wearablesToEquip, _depositIds); } + ///@notice Allow the owner of a claimed aavegotchi to equip/unequip wearables to his aavegotchis in batch + ///@dev Arrays in _wearablesToEquip need to be the same length as _tokenIds + ///@dev _wearablesToEquip are equiped to aavegotchis in the order of _tokenIds + ///@param _tokenIds Array containing the identifiers of the aavegotchis to make changes to + ///@param _wearablesToEquip An array of arrays containing the identifiers of the wearables to equip for aavegotchi in _tokenIds + function batchEquipWearables( + uint256[] calldata _tokenIds, + uint16[EQUIPPED_WEARABLE_SLOTS][] calldata _wearablesToEquip + ) external { + require(_wearablesToEquip.length == _tokenIds.length, "ItemsFacet: _wearablesToEquip length not same as _tokenIds length"); + for (uint256 i = 0; i < _tokenIds.length; i++) { + equipWearables(_tokenIds[i], _wearablesToEquip[i]); + } + } + + ///@notice Allow the owner of a claimed aavegotchi to equip/unequip wearables to his aavegotchis in batch + ///@dev Arrays in _wearablesToEquip need to be the same length as _tokenIds + ///@dev _wearablesToEquip are equiped to aavegotchis in the order of _tokenIds + ///@dev _depositIds are equiped to aavegotchis in the order of _tokenIds + ///@param _tokenIds Array containing the identifiers of the aavegotchis to make changes to + ///@param _wearablesToEquip An array of arrays containing the identifiers of the wearables to equip for aavegotchis in _tokenIds + ///@param _depositIds An array of arrays containing the identifiers of the deposited wearables to equip for aavegotchis in _tokenIds + function batchEquipDelegatedWearables( + uint256[] calldata _tokenIds, + uint16[EQUIPPED_WEARABLE_SLOTS][] calldata _wearablesToEquip, + uint256[EQUIPPED_WEARABLE_SLOTS][] calldata _depositIds + ) external { + require(_wearablesToEquip.length == _tokenIds.length, "ItemsFacet: _wearablesToEquip length not same as _tokenIds length"); + require(_depositIds.length == _tokenIds.length, "ItemsFacet: _depositIds length not same as _tokenIds length"); + for (uint256 i = 0; i < _tokenIds.length; i++) { + equipDelegatedWearables(_tokenIds[i], _wearablesToEquip[i], _depositIds[i]); + } + } + function _equipWearables( uint256 _tokenId, uint16[EQUIPPED_WEARABLE_SLOTS] calldata _wearablesToEquip, From 018b90586e32e5fc3c5954549a3b8770110ecfbc Mon Sep 17 00:00:00 2001 From: pgendreau Date: Tue, 10 Dec 2024 19:19:06 -0500 Subject: [PATCH 2/8] AGIP135: add WearablesConfigFacet with tests and upgrade script --- .../facets/WearablesConfigFacet.sol | 155 ++++++++ .../Aavegotchi/libraries/LibAppStorage.sol | 9 + .../libraries/LibWearablesConfig.sol | 56 +++ scripts/deployFullDiamond.ts | 3 +- .../upgrades/upgrade-wearablesConfigFacet.ts | 58 +++ test/wearablesConfigTest.ts | 364 ++++++++++++++++++ 6 files changed, 644 insertions(+), 1 deletion(-) create mode 100644 contracts/Aavegotchi/facets/WearablesConfigFacet.sol create mode 100644 contracts/Aavegotchi/libraries/LibWearablesConfig.sol create mode 100644 scripts/upgrades/upgrade-wearablesConfigFacet.ts create mode 100644 test/wearablesConfigTest.ts diff --git a/contracts/Aavegotchi/facets/WearablesConfigFacet.sol b/contracts/Aavegotchi/facets/WearablesConfigFacet.sol new file mode 100644 index 00000000..7f4599be --- /dev/null +++ b/contracts/Aavegotchi/facets/WearablesConfigFacet.sol @@ -0,0 +1,155 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.1; + +import {LibMeta} from "../../shared/libraries/LibMeta.sol"; +import {Modifiers, WearablesConfig, EQUIPPED_WEARABLE_SLOTS} from "../libraries/LibAppStorage.sol"; +import {LibAavegotchi} from "../libraries/LibAavegotchi.sol"; +import {LibWearablesConfig} from "../libraries/LibWearablesConfig.sol"; + +contract WearablesConfigFacet is Modifiers { + + /** + * @dev The number of free slots available for wearables configurations. + */ + uint8 constant WEARABLESCONFIG_FREE_SLOTS = 3; + /** + * @dev The price to create a wearable config slot once the free slots have been reached. + */ + uint256 constant WEARABLESCONFIG_SLOT_PRICE = 1000000000000000000; // 1 GHST + + uint256 constant MAX_UINT8 = 2**8 - 1; + + event WearablesConfigCreated(address indexed owner, uint256 indexed tokenId, uint8 indexed wearablesConfigId, uint16[EQUIPPED_WEARABLE_SLOTS] wearables, uint256 value); + event WearablesConfigUpdated(address indexed owner, uint256 indexed tokenId, uint8 indexed wearablesConfigId, uint16[EQUIPPED_WEARABLE_SLOTS] wearables); + event WearablesConfigPaymentReceived(address indexed owner, uint256 indexed tokenId, uint8 indexed wearablesConfigId, uint256 value); + + /** + * @notice Creates and stores a new wearables configuration (max 255 per Aavegotchi per owner). + * @param _tokenId The ID of the aavegotchi to create the wearables configuration for. + * @param _name The name of the wearables configuration. + * @param _wearablesToStore The wearables to store for this wearables configuration. + * @return wearablesConfigId The ID of the newly created wearables configuration. + */ + function createWearablesConfig( + uint256 _tokenId, + string calldata _name, + uint16[EQUIPPED_WEARABLE_SLOTS] calldata _wearablesToStore + ) + external + payable + onlyAavegotchiOwner(_tokenId) + returns (uint8 wearablesConfigId) + { + // check that the aavegotchi has been summoned + require(s.aavegotchis[_tokenId].status == LibAavegotchi.STATUS_AAVEGOTCHI, "WearablesConfigFacet: Can only create wearables config for Aavegotchi"); + // check that wearables are valid and for the right slots + require(LibWearablesConfig._checkValidWearables(_wearablesToStore), "WearablesConfigFacet: Invalid wearables"); + // check that name is not empty + require(bytes(_name).length > 0, "WearablesConfigFacet: WearablesConfig name cannot be blank"); + + address owner = LibMeta.msgSender(); + + // get the next available slot + uint8 newWearablesConfigId = LibWearablesConfig._getNextWearablesConfigId(owner, _tokenId); + // solidity will throw if slots used overflows + require(newWearablesConfigId < 2**8 - 1, "WearablesConfigFacet: No more wearables config slots available"); + + // create the new wearables config and add it to the gotchi for that owner + WearablesConfig memory newWearablesConfig = WearablesConfig({name: _name, wearables: _wearablesToStore}); + s.gotchiWearableConfigs[_tokenId][owner].push(newWearablesConfig); + s.ownerGotchiSlotsUsed[owner][_tokenId] += 1; + + // if the ownner has reached the free slots limit then they need to pay for the extra slot + if (newWearablesConfigId >= WEARABLESCONFIG_FREE_SLOTS) { + require(msg.value == WEARABLESCONFIG_SLOT_PRICE, "WearablesConfigFacet: Incorrect GHST value sent"); + + // send GHST to the dao treasury + (bool success, ) = payable(s.daoTreasury).call{value: msg.value}(""); + require(success, "WearablesConfigFacet: Failed to send GHST to DAO treasury"); + + emit WearablesConfigPaymentReceived(owner, _tokenId, newWearablesConfigId, msg.value); + } + + emit WearablesConfigCreated(owner, _tokenId, newWearablesConfigId, _wearablesToStore, msg.value); + + return newWearablesConfigId; + } + + /// @notice Updates the wearables config for the given wearables config id + /// @param _tokenId The ID of the aavegotchi to update the wearables configuration for + /// @param _wearablesConfigId The ID of the wearables configuration to update + /// @param _name The name of the wearables configuration + /// @param _wearablesToStore The wearables to store + /// @dev if _name is empty, only wearables are updated. + function updateWearablesConfig( + uint256 _tokenId, + uint8 _wearablesConfigId, + string calldata _name, + uint16[EQUIPPED_WEARABLE_SLOTS] calldata _wearablesToStore + ) + external + onlyAavegotchiOwner(_tokenId) + { + address owner = LibMeta.msgSender(); + + require(LibWearablesConfig._checkValidWearables(_wearablesToStore), "WearablesConfigFacet: Invalid wearables"); + require(LibWearablesConfig._wearablesConfigExists(owner, _tokenId, _wearablesConfigId), "WearablesConfigFacet: invalid id, WearablesConfig not found"); + + // skip if name is empty + if (bytes(_name).length > 0) { + s.gotchiWearableConfigs[_tokenId][owner][_wearablesConfigId].name = _name; + } + + // update the wearables + s.gotchiWearableConfigs[_tokenId][owner][_wearablesConfigId].wearables = _wearablesToStore; + + emit WearablesConfigUpdated(owner, _tokenId, _wearablesConfigId, _wearablesToStore); + } + + /// @notice Returns true if the given wearables config id exists for the given aavegotchi + /// @param _owner The owner of the aavegotchi + /// @param _tokenId The ID of the aavegotchi to update the wearables configuration for + /// @param _wearablesConfigId The ID of the wearables configuration to update + /// @return exists true if the wearables config exists + function wearablesConfigExists(address _owner, uint256 _tokenId, uint8 _wearablesConfigId) external view returns (bool exists) { + exists = LibWearablesConfig._wearablesConfigExists(_owner, _tokenId, _wearablesConfigId); + } + + /// @notice Returns the wearables config for the given wearables config id + /// @param _owner The owner of the aavegotchi + /// @param _tokenId The ID of the aavegotchi to update the wearables configuration for + /// @param _wearablesConfigId The ID of the wearables configuration to update + /// @return wearablesConfig The wearables config + function getWearablesConfig(address _owner, uint256 _tokenId, uint8 _wearablesConfigId) external view returns (WearablesConfig memory wearablesConfig) { + require(LibWearablesConfig._wearablesConfigExists(_owner, _tokenId, _wearablesConfigId), "WearablesConfigFacet: invalid id, WearablesConfig not found"); + return s.gotchiWearableConfigs[_tokenId][_owner][_wearablesConfigId]; + } + + /// @notice Returns the name of the wearables config for the given wearables config id + /// @param _owner The owner of the aavegotchi + /// @param _tokenId The ID of the aavegotchi to update the wearables configuration for + /// @param _wearablesConfigId The ID of the wearables configuration to update + /// @return name The name of the wearables config + function getWearablesConfigName(address _owner, uint256 _tokenId, uint8 _wearablesConfigId) external view returns (string memory name) { + require(LibWearablesConfig._wearablesConfigExists(_owner, _tokenId, _wearablesConfigId), "WearablesConfigFacet: invalid id, WearablesConfig not found"); + return s.gotchiWearableConfigs[_tokenId][_owner][_wearablesConfigId].name; + } + + /// @notice Returns the wearables for the given wearables config id + /// @param _owner The owner of the aavegotchi + /// @param _tokenId The ID of the aavegotchi to update the wearables configuration for + /// @param _wearablesConfigId The ID of the wearables configuration to update + /// @return wearables The wearables stored for this wearables config + function getWearablesConfigWearables(address _owner, uint256 _tokenId, uint8 _wearablesConfigId) external view returns (uint16[EQUIPPED_WEARABLE_SLOTS] memory wearables) { + require(LibWearablesConfig._wearablesConfigExists(_owner, _tokenId, _wearablesConfigId), "WearablesConfigFacet: invalid id, WearablesConfig not found"); + return s.gotchiWearableConfigs[_tokenId][_owner][_wearablesConfigId].wearables; + } + + /// @notice Returns the number of wearables configs for the given aavegotchi for this owner + /// @param _owner The owner of the aavegotchi + /// @param _tokenId The ID of the aavegotchi to update the wearables configuration for + /// @return slotsUsed The number of wearables configs + function getAavegotchiWearablesConfigCount(address _owner, uint256 _tokenId) external view returns (uint256 slotsUsed) { + return s.ownerGotchiSlotsUsed[_owner][_tokenId]; + } +} diff --git a/contracts/Aavegotchi/libraries/LibAppStorage.sol b/contracts/Aavegotchi/libraries/LibAppStorage.sol index 09172bd9..833cd360 100644 --- a/contracts/Aavegotchi/libraries/LibAppStorage.sol +++ b/contracts/Aavegotchi/libraries/LibAppStorage.sol @@ -252,6 +252,11 @@ struct ERC1155BuyOrder { bool completed; } +struct WearablesConfig { + string name; + uint16[EQUIPPED_WEARABLE_SLOTS] wearables; +} + struct AppStorage { mapping(address => AavegotchiCollateralTypeInfo) collateralTypeInfo; mapping(address => uint256) collateralTypeIndexes; @@ -387,6 +392,10 @@ struct AppStorage { mapping(uint256 => ERC1155BuyOrder) erc1155BuyOrders; // buyOrderId => data address gotchGeistBridge; address itemGeistBridge; + // gotchi => owner => wearable configs + mapping(uint256 => mapping(address => WearablesConfig[])) gotchiWearableConfigs; + // owner => gotchi => slots used + mapping(address => mapping (uint256 => uint8)) ownerGotchiSlotsUsed; } library LibAppStorage { diff --git a/contracts/Aavegotchi/libraries/LibWearablesConfig.sol b/contracts/Aavegotchi/libraries/LibWearablesConfig.sol new file mode 100644 index 00000000..c4912e4f --- /dev/null +++ b/contracts/Aavegotchi/libraries/LibWearablesConfig.sol @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.1; + +import {LibMeta} from "../../shared/libraries/LibMeta.sol"; +import {LibItems} from "../libraries/LibItems.sol"; +import {LibAppStorage, AppStorage, WearablesConfig, ItemType, EQUIPPED_WEARABLE_SLOTS} from "../libraries/LibAppStorage.sol"; + +library LibWearablesConfig { + + /// @notice Returns the next wearables config id for that gotchi given that owner + /// @param _owner The owner of the gotchi + /// @param _tokenId The tokenId of the gotchi + /// @return nextWearablesConfigId The next free wearables config id + function _getNextWearablesConfigId(address _owner, uint256 _tokenId) internal view returns (uint8 nextWearablesConfigId) { + AppStorage storage s = LibAppStorage.diamondStorage(); + // slots start at 0 so slotsUsed is always the next config id + nextWearablesConfigId = s.ownerGotchiSlotsUsed[_owner][_tokenId]; + } + + + /// @notice Checks if a wearables config exists for a gotchi given an owner + /// @param _owner The owner of the gotchi + /// @param _tokenId The tokenId of the gotchi + /// @param _wearablesConfigId The wearables config id + /// @return exists True if the wearables config exists false otherwise + function _wearablesConfigExists(address _owner, uint256 _tokenId, uint8 _wearablesConfigId) internal view returns (bool exists) { + AppStorage storage s = LibAppStorage.diamondStorage(); + // slots start at 0 so slots used should always be greater by 1 than the last config id + exists = (s.ownerGotchiSlotsUsed[_owner][_tokenId] > _wearablesConfigId); + } + + /// @notice Checks if a wearables configuration consist of valid wearables and are for the correct slot + /// @param _wearablesToStore The wearables to store + /// @return valid True if the wearables configuration is valid and false otherwise + function _checkValidWearables(uint16[EQUIPPED_WEARABLE_SLOTS] memory _wearablesToStore) internal view returns (bool valid) { + AppStorage storage s = LibAppStorage.diamondStorage(); + uint256 itemTypesLength = s.itemTypes.length; + bool valid = true; + for (uint256 slot; slot < EQUIPPED_WEARABLE_SLOTS; slot++) { + uint256 toStoreId = _wearablesToStore[slot]; + if (toStoreId != 0) { + require(itemTypesLength > toStoreId, "LibWearablesConfig: Item type does not exist"); + ItemType storage itemType = s.itemTypes[toStoreId]; + if (itemType.category != LibItems.ITEM_CATEGORY_WEARABLE) { + valid = false; + break; + } + if (itemType.slotPositions[slot] == false) { + valid = false; + break; + } + } + } + return valid; + } +} diff --git a/scripts/deployFullDiamond.ts b/scripts/deployFullDiamond.ts index 886650cf..4e764f6e 100644 --- a/scripts/deployFullDiamond.ts +++ b/scripts/deployFullDiamond.ts @@ -773,7 +773,7 @@ export async function deployFullDiamond(useFreshDeploy: boolean = false) { diamondName: "AavegotchiDiamond", initDiamond: "contracts/Aavegotchi/InitDiamond.sol:InitDiamond", facetNames: [ - "contracts/Aavegotchi/facets/BridgeFacet.sol:BridgeFacet", + //"contracts/Aavegotchi/facets/BridgeFacet.sol:BridgeFacet", "contracts/Aavegotchi/facets/AavegotchiFacet.sol:AavegotchiFacet", "AavegotchiGameFacet", "SvgFacet", @@ -799,6 +799,7 @@ export async function deployFullDiamond(useFreshDeploy: boolean = false) { "ItemsRolesRegistryFacet", "ERC1155BuyOrderFacet", "PolygonXGeistBridgeFacet", + "WearablesConfigFacet", ], owner: ownerAddress, args: initArgs, diff --git a/scripts/upgrades/upgrade-wearablesConfigFacet.ts b/scripts/upgrades/upgrade-wearablesConfigFacet.ts new file mode 100644 index 00000000..43e78f18 --- /dev/null +++ b/scripts/upgrades/upgrade-wearablesConfigFacet.ts @@ -0,0 +1,58 @@ +import { ethers, run } from "hardhat"; +import { + convertFacetAndSelectorsToString, + DeployUpgradeTaskArgs, + FacetsAndAddSelectors, +} from "../../tasks/deployUpgrade"; +import { + diamondOwner, + maticDiamondAddress, +} from "../helperFunctions"; + +export async function upgrade() { + const facets: FacetsAndAddSelectors[] = [ + { + facetName: "WearablesConfigFacet", + addSelectors: [ + 'function createWearablesConfig(uint256 _tokenId, string _name, uint16[16] _wearablesToStore) external payable', + 'function updateWearablesConfig(uint256 _tokenId, uint8 _wearablesConfigId, string _name, uint16[16] _wearablesToStore) external', + 'function getWearablesConfig(address _owner, uint256 _tokenId, uint8 _wearablesConfigId) external view', + 'function getWearablesConfigName(address _owner, uint256 _tokenId, uint8 _wearablesConfigId) external view', + 'function getWearablesConfigWearables(address _owner, uint256 _tokenId, uint8 _wearablesConfigId) external view', + 'function getAavegotchiWearablesConfigCount(address _owner, uint256 _tokenId) external view', + 'function wearablesConfigExists(address _owner, uint256 _tokenId, uint8 _wearablesConfigId) external view', + ], + removeSelectors: [], + }, + ]; + + const joined = convertFacetAndSelectorsToString(facets); + + const diamondOwnerAddress = await diamondOwner(maticDiamondAddress, ethers) + + // todo: use deploymentConfig to detect args for Polter/Geist + const args: DeployUpgradeTaskArgs = { + //diamondOwner: "0xd38Df837a1EAd12ee16f8b8b7E5F58703f841668", // polter-testnet + //diamondAddress: '0x226625C1B1174e7BaaE8cDC0432Db0e2ED83b7Ba', // polter-testnet + diamondAddress: maticDiamondAddress, + diamondOwner: diamondOwnerAddress, + facetsAndAddSelectors: joined, + useLedger: false, + useMultisig: false, + freshDeployment: true, + // initAddress + // initCalldata + }; + + await run("deployUpgrade", args); +} + +if (require.main === module) { + upgrade() + .then(() => process.exit(0)) + // .then(() => console.log('upgrade completed') /* process.exit(0) */) + .catch((error) => { + console.error(error); + process.exit(1); + }); +} diff --git a/test/wearablesConfigTest.ts b/test/wearablesConfigTest.ts new file mode 100644 index 00000000..41a56fd2 --- /dev/null +++ b/test/wearablesConfigTest.ts @@ -0,0 +1,364 @@ +/* global describe it before ethers network */ +/* eslint prefer-const: "off" */ + +//@ts-ignore +import { ethers, network } from "hardhat"; +import chai from "chai"; +import { upgrade } from "../scripts/upgrades/upgrade-wearablesConfigFacet"; +import { impersonate, resetChain } from "../scripts/helperFunctions"; +import { + AavegotchiFacet, + WearablesConfigFacet, +} from "../typechain"; +import * as helpers from "@nomicfoundation/hardhat-network-helpers"; + +const { expect } = chai; + +describe("Testing Wearables Config", async function () { + this.timeout(300000); + + const diamondAddress = "0x86935F11C86623deC8a25696E1C19a8659CbF95d"; // polygon mainnet + //const diamondAddress = '0x226625C1B1174e7BaaE8cDC0432Db0e2ED83b7Ba'; // polter-testnet + const slotPrice = ethers.utils.parseUnits("1", "ether"); + const wearablesToStore = [105, 209, 159, 104, 106, 65, 413, 210, 0, 0, 0, 0, 0, 0, 0, 0]; + const wearablesToStoreWithInvalidId = [418, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + const wearablesToStoreWithInvalidSlot = [104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + const aavegotchiId = 4895; + const rentedOutAavegotchiId = 9121; + const unsummonedAavegotchiId = 945; + const someoneElseAavegotchiId = 10356; + + let aavegotchiOwnerAddress: any; + let anotherAavegotchiOwnerAddress: any; + let daoAddress: any; + let aavegotchiFacet: AavegotchiFacet; + let wearablesConfigFacetWithOwner: WearablesConfigFacet; + let wearablesConfigFacetWithOtherOwner: WearablesConfigFacet; + + before(async function () { + await resetChain(hre); + // workaround for issue https://github.com/NomicFoundation/hardhat/issues/5511 + await helpers.mine() + + await upgrade(); + + aavegotchiFacet = (await ethers.getContractAt( + "contracts/Aavegotchi/facets/AavegotchiFacet.sol:AavegotchiFacet", + diamondAddress + )) as AavegotchiFacet; + + //aavegotchiOwnerAddress = await aavegotchiFacet.ownerOf(aavegotchiId); + aavegotchiOwnerAddress = "0x43FF4C088df0A425d1a519D3030A1a3DFff05CfD"; + anotherAavegotchiOwnerAddress = "0xE1bCD0f5c6c855ee3452B38E16FeD0b7Cb0CC507"; + + //const accounts = await ethers.getSigners(); + //const ownerAddress = await accounts[0].getAddress(); + //daoAddress = ownerAddress; + daoAddress = "0xb208f8BB431f580CC4b216826AFfB128cd1431aB"; + + const wearablesConfigFacet = (await ethers.getContractAt( + "WearablesConfigFacet", + diamondAddress + )) as WearablesConfigFacet; + + wearablesConfigFacetWithOwner = await impersonate( + aavegotchiOwnerAddress, + wearablesConfigFacet, + ethers, + network + ); + + wearablesConfigFacetWithOtherOwner = await impersonate( + anotherAavegotchiOwnerAddress, + wearablesConfigFacet, + ethers, + network + ); + }); + + describe("Testing createWearablesConfig", async function () { + it("Should revert if wearables list is invalid", async function () { + const invalidWearables = new Array(16).fill(1); + await expect( + wearablesConfigFacetWithOwner.createWearablesConfig(aavegotchiId, "Test", invalidWearables) + ).to.be.revertedWith( + "WearablesConfigFacet: Invalid wearables" + ); + }); + it("Should revert if wearablesConfig name is empty", async function () { + await expect( + wearablesConfigFacetWithOwner.createWearablesConfig(aavegotchiId, "", wearablesToStore) + ).to.be.revertedWith("WearablesConfigFacet: WearablesConfig name cannot be blank"); + }); + it("Should succeed to create wearablesConfig if all parameters are valid and emit event", async function () { + const receipt = await ( + // config #1 (id: 0) + await wearablesConfigFacetWithOwner.createWearablesConfig(aavegotchiId, "Test", wearablesToStore) + ).wait(); + // check that event is emitted with the right parameters + const event = receipt!.events!.find( + (event) => event.event === "WearablesConfigCreated" + ); + const owner = event!.args!.owner; + const tokenId = event!.args!.tokenId; + const wearablesConfigId = event!.args!.wearablesConfigId; + const wearables = event!.args!.wearables; + const value = event!.args!.value; + expect(owner).to.equal(aavegotchiOwnerAddress); + expect(tokenId).to.equal(aavegotchiId); + expect(wearablesConfigId).to.equal(0); + expect(wearables).to.eql(wearablesToStore); + expect(value).to.equal(0); + }); + }); + + describe("Testing getWearablesConfigName", async function () { + it("Should revert if invalid wearablesConfig id", async function () { + await expect( + wearablesConfigFacetWithOwner.getWearablesConfigName(aavegotchiOwnerAddress, aavegotchiId, 99) + ).to.be.revertedWith("WearablesConfigFacet: invalid id, WearablesConfig not found"); + }); + it("Should revert if invalid aavegotchi id for owner", async function () { + await expect( + wearablesConfigFacetWithOwner.getWearablesConfigName(aavegotchiOwnerAddress, someoneElseAavegotchiId, 0) + ).to.be.revertedWith("WearablesConfigFacet: invalid id, WearablesConfig not found"); + }); + it("Should return the name of a valid wearablesConfig id", async function () { + expect( + await wearablesConfigFacetWithOwner.getWearablesConfigName(aavegotchiOwnerAddress, aavegotchiId, 0) + ).to.equal("Test"); + }); + }); + + describe("Testing getWearablesConfigWearables", async function () { + it("Should revert if invalid wearablesConfig id", async function () { + await expect( + wearablesConfigFacetWithOwner.getWearablesConfigWearables(aavegotchiOwnerAddress, aavegotchiId, 99) + ).to.be.revertedWith("WearablesConfigFacet: invalid id, WearablesConfig not found"); + }); + it("Should revert if invalid aavegotchi id for owner", async function () { + await expect( + wearablesConfigFacetWithOwner.getWearablesConfigWearables(aavegotchiOwnerAddress, someoneElseAavegotchiId, 0) + ).to.be.revertedWith("WearablesConfigFacet: invalid id, WearablesConfig not found"); + }); + it("Should return the wearables array of a valid wearablesConfig id", async function () { + expect( + await wearablesConfigFacetWithOwner.getWearablesConfigWearables(aavegotchiOwnerAddress, aavegotchiId, 0) + ).to.eql(wearablesToStore); + }); + }); + + describe("Testing getWearablesConfig", async function () { + it("Should revert if invalid wearablesConfig id", async function () { + await expect( + wearablesConfigFacetWithOwner.getWearablesConfig(aavegotchiOwnerAddress, aavegotchiId, 99) + ).to.be.revertedWith("WearablesConfigFacet: invalid id, WearablesConfig not found"); + }); + it("Should revert if invalid aavegotchi id for owner", async function () { + await expect( + wearablesConfigFacetWithOwner.getWearablesConfig(aavegotchiOwnerAddress, someoneElseAavegotchiId, 0) + ).to.be.revertedWith("WearablesConfigFacet: invalid id, WearablesConfig not found"); + }); + it("Should return name and wearables array if valid wearablesConfig id", async function () { + const wearablesConfig = await wearablesConfigFacetWithOwner.getWearablesConfig(aavegotchiOwnerAddress, aavegotchiId, 0); + expect(wearablesConfig.name).to.equal("Test"); + expect(wearablesConfig.wearables).to.eql(wearablesToStore); + }); + }); + + describe("Testing updateWearablesConfig", async function () { + it("Should be able to update existing WearablesConfig and emit event if all parameters are valid", async function () { + const newWearablesToStore = new Array(16).fill(0); + const receipt = await ( + await wearablesConfigFacetWithOwner.updateWearablesConfig(aavegotchiId, 0, "New Name", newWearablesToStore) + ).wait(); + // check that event is emitted with the right parameters + const event = receipt!.events!.find( + (event) => event.event === "WearablesConfigUpdated" + ); + const owner = event!.args!.owner; + const tokenId = event!.args!.tokenId; + const wearablesConfigId = event!.args!.wearablesConfigId; + const wearables = event!.args!.wearables; + expect(owner).to.equal(aavegotchiOwnerAddress); + expect(tokenId).to.equal(aavegotchiId); + expect(wearablesConfigId).to.equal(0); + expect(wearables).to.eql(newWearablesToStore); + // check wearablesConfig name has been updated + expect( + await wearablesConfigFacetWithOwner.getWearablesConfigName(aavegotchiOwnerAddress, aavegotchiId, 0) + ).to.equal("New Name"); + // check wearablesConfig stored wearables have been updated + expect( + await wearablesConfigFacetWithOwner.getWearablesConfigWearables(aavegotchiOwnerAddress, aavegotchiId, 0) + ).to.eql(newWearablesToStore); + }); + it("Should not update the name if the new name is empty", async function () { + await wearablesConfigFacetWithOwner.updateWearablesConfig(aavegotchiId, 0, "", wearablesToStore) + // check wearablesConfig name has been updated + expect( + await wearablesConfigFacetWithOwner.getWearablesConfigName(aavegotchiOwnerAddress, aavegotchiId, 0) + ).to.equal("New Name"); + // check wearablesConfig stored wearables have been updated + expect( + await wearablesConfigFacetWithOwner.getWearablesConfigWearables(aavegotchiOwnerAddress, aavegotchiId, 0) + ).to.eql(wearablesToStore); + }); + it("Should revert if invalid wearablesConfig id", async function () { + await expect( + wearablesConfigFacetWithOwner.updateWearablesConfig(aavegotchiId, 99, "Test", wearablesToStore) + ).to.be.revertedWith("WearablesConfigFacet: invalid id, WearablesConfig not found"); + }); + }); + + describe("Testing wearablesConfigExists", async function () { + it("Should return true for valid wearablesConfig", async function () { + expect( + await wearablesConfigFacetWithOwner.wearablesConfigExists(aavegotchiOwnerAddress, aavegotchiId, 0) + ).to.be.true; + }); + it("Should return false for invalid wearablesConfig", async function () { + expect( + await wearablesConfigFacetWithOwner.wearablesConfigExists(aavegotchiOwnerAddress, aavegotchiId, 99) + ).to.be.false; + }); + }); + + describe("Testing getAavegotchiWearablesConfigCount", async function () { + it("Should return the right amount of wearablesConfig", async function () { + expect( + await wearablesConfigFacetWithOwner.getAavegotchiWearablesConfigCount(aavegotchiOwnerAddress, aavegotchiId) + ).to.equal(1); + // config #2 (id: 1) + await wearablesConfigFacetWithOwner.createWearablesConfig(aavegotchiId, "Test", wearablesToStore) + expect( + await wearablesConfigFacetWithOwner.getAavegotchiWearablesConfigCount(aavegotchiOwnerAddress, aavegotchiId) + ).to.equal(2); + expect( + await wearablesConfigFacetWithOwner.getAavegotchiWearablesConfigCount(aavegotchiOwnerAddress, 2499) + ).to.equal(0); + }); + }); + + describe("Testing createWearablesConfig with payment", async function () { + it("Should be able to create a 3rd for free", async function () { + // config #3 (id: 2) + await wearablesConfigFacetWithOwner.createWearablesConfig(aavegotchiId, "Test", wearablesToStore) + expect( + await wearablesConfigFacetWithOwner.getAavegotchiWearablesConfigCount(aavegotchiOwnerAddress, aavegotchiId) + ).to.equal(3); + }); + it("Should not be able to create a 4th for free", async function () { + await expect( + wearablesConfigFacetWithOwner.createWearablesConfig(aavegotchiId, "Test 4th", wearablesToStore) + ).to.be.revertedWith("WearablesConfigFacet: Incorrect GHST value sent"); + }); + it("Should not be able to create a 4th for more than 1 GHST", async function () { + await expect( + wearablesConfigFacetWithOwner.createWearablesConfig(aavegotchiId, "Test 4th", wearablesToStore, { value: ethers.utils.parseEther("10") }) + ).to.be.revertedWith("WearablesConfigFacet: Incorrect GHST value sent"); + }); + it("Should be able to pay for the 4th (with event emitted)", async function () { + const daoBalanceBefore = await ethers.provider.getBalance(daoAddress); + const receipt = await ( + // config #4 (id: 3) + await wearablesConfigFacetWithOwner.createWearablesConfig(aavegotchiId, "Test 4th", wearablesToStore, { value: ethers.utils.parseEther("1") }) + ).wait(); + // check that event is emitted with the right parameters + const event = receipt!.events!.find( + (event) => event.event === "WearablesConfigPaymentReceived" + ); + const owner = event!.args!.owner; + const tokenId = event!.args!.tokenId; + const wearablesConfigId = event!.args!.wearablesConfigId; + const value = event!.args!.value; + expect(owner).to.equal(aavegotchiOwnerAddress); + expect(tokenId).to.equal(aavegotchiId); + expect(wearablesConfigId).to.equal(3); + expect(value).to.equal(ethers.utils.parseEther("1")); + // compare balance before and after for dao address + const daoBalanceAfter = await ethers.provider.getBalance(daoAddress); + expect(daoBalanceAfter).to.equal(daoBalanceBefore.add(ethers.utils.parseEther("1"))); + expect( + await wearablesConfigFacetWithOwner.getWearablesConfigName(aavegotchiOwnerAddress, aavegotchiId, 3) + ).to.equal("Test 4th"); + }); + }); + describe("Testing for rented gotchis", async function () { + it("Should be able to save for a rented gotchi", async function () { + // config #5 (id: 4) + await wearablesConfigFacetWithOtherOwner.createWearablesConfig(rentedOutAavegotchiId, "Test Rental", wearablesToStore, { value: ethers.utils.parseEther("1") }) + expect( + await wearablesConfigFacetWithOwner.getWearablesConfigName(anotherAavegotchiOwnerAddress, rentedOutAavegotchiId, 0) + ).to.equal("Test Rental"); + }); + it("Should be able to update for a rented gotchi", async function () { + const newWearablesToStore = new Array(16).fill(0); + await wearablesConfigFacetWithOtherOwner.updateWearablesConfig(rentedOutAavegotchiId, 0, "Test Update Rental", newWearablesToStore) + expect( + await wearablesConfigFacetWithOwner.getWearablesConfigName(anotherAavegotchiOwnerAddress, rentedOutAavegotchiId, 0) + ).to.equal("Test Update Rental"); + expect( + await wearablesConfigFacetWithOwner.getWearablesConfigWearables(anotherAavegotchiOwnerAddress, rentedOutAavegotchiId, 0) + ).to.eql(newWearablesToStore); + }); + it("Should revert on create for rented out gotchi", async function () { + await expect( + wearablesConfigFacetWithOwner.createWearablesConfig(rentedOutAavegotchiId, "Test Rented Out", wearablesToStore, { value: ethers.utils.parseEther("1") }) + ).to.be.revertedWith("LibAppStorage: Only aavegotchi owner can call this function"); + }); + }); + describe("Testing for invalid gotchis", async function () { + it("Should revert for a portal", async function () { + await expect( + wearablesConfigFacetWithOwner.createWearablesConfig(unsummonedAavegotchiId, "Test Portal", wearablesToStore, { value: ethers.utils.parseEther("1") }) + ).to.be.revertedWith("WearablesConfigFacet: Can only create wearables config for Aavegotchi"); + }); + it("Should revert for a gotchi not owned (create)", async function () { + await expect( + wearablesConfigFacetWithOwner.createWearablesConfig(someoneElseAavegotchiId, "Test CreateAavegotchi not owned", wearablesToStore, { value: ethers.utils.parseEther("1") }) + ).to.be.revertedWith("LibAppStorage: Only aavegotchi owner can call this function"); + }); + it("Should revert for a gotchi not owned (update)", async function () { + await expect( + wearablesConfigFacetWithOwner.updateWearablesConfig(someoneElseAavegotchiId, 0, "", wearablesToStore) + ).to.be.revertedWith("LibAppStorage: Only aavegotchi owner can call this function"); + }); + }); + describe("Testing for invalid wearables", async function () { + it("Should revert for an invalid wearable id (create)", async function () { + await expect( + wearablesConfigFacetWithOwner.createWearablesConfig(aavegotchiId, "Test Invalid Wearable Id", wearablesToStoreWithInvalidId, { value: ethers.utils.parseEther("1") }) + ).to.be.revertedWith("LibWearablesConfig: Item type does not exist"); + }); + it("Should revert for an invalid wearable id (update)", async function () { + await expect( + wearablesConfigFacetWithOwner.updateWearablesConfig(aavegotchiId, 0, "", wearablesToStoreWithInvalidId) + ).to.be.revertedWith("LibWearablesConfig: Item type does not exist"); + }); + it("Should revert for an invalid wearable slot (create)", async function () { + await expect( + wearablesConfigFacetWithOwner.createWearablesConfig(aavegotchiId, "Test Invalid Wearable Slot", wearablesToStoreWithInvalidSlot, { value: ethers.utils.parseEther("1") }) + ).to.be.revertedWith("WearablesConfigFacet: Invalid wearables"); + }); + it("Should revert for an invalid wearable slot (update)", async function () { + await expect( + wearablesConfigFacetWithOwner.updateWearablesConfig(aavegotchiId, 0, "", wearablesToStoreWithInvalidSlot) + ).to.be.revertedWith("WearablesConfigFacet: Invalid wearables"); + }); + }); + describe("Testing for max wearable configs", async function () { + it("Shouldn't be able to create more than 255", async function () { + for (let i = 4; i < 255; i++) { + // config #5-255 (id: 4-254) + await wearablesConfigFacetWithOwner.createWearablesConfig(aavegotchiId, "Test", wearablesToStore, { value: ethers.utils.parseEther("1") }); + } + expect( + await wearablesConfigFacetWithOwner.getAavegotchiWearablesConfigCount(aavegotchiOwnerAddress, aavegotchiId) + ).to.equal(255); + await expect( + wearablesConfigFacetWithOwner.createWearablesConfig(aavegotchiId, "Test 256th", wearablesToStore, { value: ethers.utils.parseEther("1") }) + ).to.be.revertedWith("WearablesConfigFacet: No more wearables config slots available"); + }); + }); +}); From 9bae4eb56e7069e8a98930f10a6813f7eba255a4 Mon Sep 17 00:00:00 2001 From: pgendreau Date: Tue, 31 Dec 2024 19:05:04 -0500 Subject: [PATCH 3/8] AGIP135: add tests for batchEquipWearables and batcheEquipDelegatedWearables and upgrade script for ItemsFacet --- scripts/upgrades/upgrade-itemsFacet.ts | 41 +++++ test/itemsFacetBatchEquip.ts | 213 +++++++++++++++++++++++++ 2 files changed, 254 insertions(+) create mode 100644 scripts/upgrades/upgrade-itemsFacet.ts create mode 100644 test/itemsFacetBatchEquip.ts diff --git a/scripts/upgrades/upgrade-itemsFacet.ts b/scripts/upgrades/upgrade-itemsFacet.ts new file mode 100644 index 00000000..813cfb99 --- /dev/null +++ b/scripts/upgrades/upgrade-itemsFacet.ts @@ -0,0 +1,41 @@ +import { ethers, run } from "hardhat"; +import { + convertFacetAndSelectorsToString, + DeployUpgradeTaskArgs, + FacetsAndAddSelectors, +} from "../../tasks/deployUpgrade"; +import { maticDiamondAddress, maticDiamondUpgrader } from "../helperFunctions"; + +export async function upgrade() { + const facets: FacetsAndAddSelectors[] = [ + { + facetName: "contracts/Aavegotchi/facets/ItemsFacet.sol:ItemsFacet", + addSelectors: [ + "function batchEquipWearables(uint256[] _tokenIds, uint16[16][] _wearablesToEquip)", + "function batchEquipDelegatedWearables(uint256[] _tokenIds, uint16[16][] _wearablesToEquip, uint256[16][] _depositIds)", + ], + removeSelectors: [], + }, + ]; + + const joined = convertFacetAndSelectorsToString(facets); + + const args: DeployUpgradeTaskArgs = { + diamondAddress: maticDiamondAddress, + diamondOwner: maticDiamondUpgrader, + facetsAndAddSelectors: joined, + useLedger: false, + useMultisig: false, + }; + + await run("deployUpgrade", args); +} + +if (require.main === module) { + upgrade() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); +} diff --git a/test/itemsFacetBatchEquip.ts b/test/itemsFacetBatchEquip.ts new file mode 100644 index 00000000..fb9f0bbd --- /dev/null +++ b/test/itemsFacetBatchEquip.ts @@ -0,0 +1,213 @@ +/* global describe it before ethers network */ +/* eslint prefer-const: "off" */ + +//@ts-ignore +import { ethers, network } from "hardhat"; +import chai from "chai"; +import { upgrade } from "../scripts/upgrades/upgrade-itemsFacet"; +import { impersonate, resetChain } from "../scripts/helperFunctions"; +import { + AavegotchiFacet, + ItemsFacet, +} from "../typechain"; +import * as helpers from "@nomicfoundation/hardhat-network-helpers"; +import { BigNumber, BigNumberish } from "ethers"; + +const { expect } = chai; + +describe("Testing Batch Equip Wearables", async function () { + this.timeout(300000); + + const diamondAddress = "0x86935F11C86623deC8a25696E1C19a8659CbF95d"; // polygon mainnet + //const diamondAddress = '0x226625C1B1174e7BaaE8cDC0432Db0e2ED83b7Ba'; // polter-testnet + const wearables = [105, 209, 159, 104, 106, 65, 413, 210, 0, 0, 0, 0, 0, 0, 0, 0]; + const wearablesWithInvalidId = [418, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + const wearablesWithInvalidSlot = [104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + const wearablesWithRentals = [222, 146, 117, 147, 6, 148, 151, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]; + const depositIdsOfWearableRentals = [0, 384, 385, 1393, 380, 382, 383, 0, 0, 0, 0, 0, 0, 0, 0, 0] + const emptyWearables = new Array(16).fill(0); + const aavegotchiId = 4895; + const wearablesRentalAavegotchiId = 3166; + + let aavegotchiOwnerAddress: any; + let anotherAavegotchiOwnerAddress: any; + let gotchisOfOwner: number[]; + let aavegotchiFacet: AavegotchiFacet; + let itemsFacetWithOwner: ItemsFacet; + let itemsFacetWithOtherOwner: ItemsFacet; + + before(async function () { + await resetChain(hre); + // workaround for issue https://github.com/NomicFoundation/hardhat/issues/5511 + await helpers.mine() + + await upgrade(); + + aavegotchiFacet = (await ethers.getContractAt( + "contracts/Aavegotchi/facets/AavegotchiFacet.sol:AavegotchiFacet", + diamondAddress + )) as AavegotchiFacet; + + const itemsFacet = (await ethers.getContractAt( + "contracts/Aavegotchi/facets/ItemsFacet.sol:ItemsFacet", + diamondAddress + )) as ItemsFacet; + + aavegotchiOwnerAddress = await aavegotchiFacet.ownerOf(aavegotchiId); + anotherAavegotchiOwnerAddress = await aavegotchiFacet.ownerOf(wearablesRentalAavegotchiId); + gotchisOfOwner = await aavegotchiFacet.tokenIdsOfOwner(aavegotchiOwnerAddress); + + //const accounts = await ethers.getSigners(); + //const ownerAddress = await accounts[0].getAddress(); + + itemsFacetWithOwner = await impersonate( + aavegotchiOwnerAddress, + itemsFacet, + ethers, + network + ); + + itemsFacetWithOtherOwner = await impersonate( + anotherAavegotchiOwnerAddress, + itemsFacet, + ethers, + network + ); + }); + + describe("Testing batch functions to equip wearables", async function () { + + async function getWearables(_tokenId: number) : number[] { + const currentWearables = await itemsFacetWithOwner.equippedWearables(_tokenId); + return currentWearables; + } + + it("Should unequip all wearables from multiple gotchis", async function () { + await itemsFacetWithOwner.batchEquipWearables( + [ + gotchisOfOwner[3], + gotchisOfOwner[4], + gotchisOfOwner[5], + ], + [ + emptyWearables, + emptyWearables, + emptyWearables, + ] + ); + expect(await getWearables(gotchisOfOwner[3])).to.deep.equal(emptyWearables); + expect(await getWearables(gotchisOfOwner[4])).to.deep.equal(emptyWearables); + expect(await getWearables(gotchisOfOwner[5])).to.deep.equal(emptyWearables); + }); + it("Should be able to unequip and requip the same gotchi", async function () { + await itemsFacetWithOwner.batchEquipWearables( + [ + aavegotchiId, + aavegotchiId, + ], + [ + emptyWearables, + wearables, + ] + ); + expect(await getWearables(aavegotchiId)).to.deep.equal(wearables); + }); + it("Should be able to unequip from one gotchi to equip on the next", async function () { + // use H1 gotchis because of background + await itemsFacetWithOwner.batchEquipWearables( + [ + gotchisOfOwner[2], + gotchisOfOwner[9], + ], + [ + emptyWearables, + wearables, + ] + ); + expect(await getWearables(gotchisOfOwner[2])).to.deep.equal(emptyWearables); + expect(await getWearables(gotchisOfOwner[9])).to.deep.equal(wearables); + }); + it("Should be able to completely flip wearables between two gotchis", async function () { + const firstGotchiId = gotchisOfOwner[13]; + const secondGotchiId = gotchisOfOwner[14]; + const firstGotchiWearables = await itemsFacetWithOwner.equippedWearables(firstGotchiId); + const secondGotchiWearables = await itemsFacetWithOwner.equippedWearables(secondGotchiId); + await itemsFacetWithOwner.batchEquipWearables( + [ + firstGotchiId, + secondGotchiId, + firstGotchiId, + secondGotchiId, + ], + [ + emptyWearables, + emptyWearables, + secondGotchiWearables, + firstGotchiWearables, + ] + ); + expect(await getWearables(firstGotchiId)).to.deep.equal(secondGotchiWearables); + expect(await getWearables(secondGotchiId)).to.deep.equal(firstGotchiWearables); + }); + it("Should unequip and requip wearables with rentals ", async function () { + await itemsFacetWithOtherOwner.batchEquipDelegatedWearables( + [ + wearablesRentalAavegotchiId, + wearablesRentalAavegotchiId, + ], + [ + emptyWearables, + wearablesWithRentals, + ], + [ + emptyWearables, + depositIdsOfWearableRentals, + ] + ); + expect(await getWearables(wearablesRentalAavegotchiId)).to.deep.equal(wearablesWithRentals); + }); + it("Should revert if arguments are not all the same length ", async function () { + await expect( + itemsFacetWithOwner.batchEquipWearables( + [ + gotchisOfOwner[2], + gotchisOfOwner[9], + ], + [ + emptyWearables, + ] + ) + ).to.be.revertedWith("ItemsFacet: _wearablesToEquip length not same as _tokenIds length"); + await expect( + itemsFacetWithOtherOwner.batchEquipDelegatedWearables( + [ + wearablesRentalAavegotchiId, + wearablesRentalAavegotchiId, + ], + [ + emptyWearables, + ], + [ + emptyWearables, + emptyWearables, + ] + ) + ).to.be.revertedWith("ItemsFacet: _wearablesToEquip length not same as _tokenIds length"); + await expect( + itemsFacetWithOtherOwner.batchEquipDelegatedWearables( + [ + wearablesRentalAavegotchiId, + wearablesRentalAavegotchiId, + ], + [ + emptyWearables, + emptyWearables, + ], + [ + emptyWearables, + ] + ) + ).to.be.revertedWith("ItemsFacet: _depositIds length not same as _tokenIds length"); + }); + }); +}); From d055ffd58bffc74d947e3ff8c0036c2c0bac6290 Mon Sep 17 00:00:00 2001 From: pgendreau Date: Sun, 12 Jan 2025 21:31:45 -0500 Subject: [PATCH 4/8] AGIP135: update fork block number to use bridged aavegotchis/wearables --- hardhat.config.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hardhat.config.ts b/hardhat.config.ts index b8cb1b8e..30e8cf8a 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -65,10 +65,11 @@ export default { }, networks: { hardhat: { + allowUnlimitedContractSize: true, // for testing forking: { url: process.env.GEIST_URL, // timeout: 12000000, - blockNumber: 1743308, + blockNumber: 2523699, }, blockGasLimit: 20000000, timeout: 120000, From 56a8144836680b07824f868539419293bf8bfdc6 Mon Sep 17 00:00:00 2001 From: pgendreau Date: Sun, 12 Jan 2025 21:44:29 -0500 Subject: [PATCH 5/8] AGIP135: update to deploy and test on Geist with brdiged gotchi and wearables --- scripts/upgrades/upgrade-itemsFacet.ts | 10 +- .../upgrades/upgrade-wearablesConfigFacet.ts | 16 +- test/itemsFacetBatchEquip.ts | 355 ++++++++++++------ test/wearablesConfigTest.ts | 178 ++++++--- 4 files changed, 380 insertions(+), 179 deletions(-) diff --git a/scripts/upgrades/upgrade-itemsFacet.ts b/scripts/upgrades/upgrade-itemsFacet.ts index 813cfb99..9672af43 100644 --- a/scripts/upgrades/upgrade-itemsFacet.ts +++ b/scripts/upgrades/upgrade-itemsFacet.ts @@ -4,7 +4,7 @@ import { DeployUpgradeTaskArgs, FacetsAndAddSelectors, } from "../../tasks/deployUpgrade"; -import { maticDiamondAddress, maticDiamondUpgrader } from "../helperFunctions"; +import { loadDeploymentConfig } from "../deployFullDiamond"; export async function upgrade() { const facets: FacetsAndAddSelectors[] = [ @@ -18,11 +18,15 @@ export async function upgrade() { }, ]; + const deploymentConfig = loadDeploymentConfig(63157); + const diamondAddress = deploymentConfig.aavegotchiDiamond as string; + const diamondOwner = deploymentConfig.itemManagers[0] as string; + const joined = convertFacetAndSelectorsToString(facets); const args: DeployUpgradeTaskArgs = { - diamondAddress: maticDiamondAddress, - diamondOwner: maticDiamondUpgrader, + diamondAddress: diamondAddress, + diamondOwner: diamondOwner, facetsAndAddSelectors: joined, useLedger: false, useMultisig: false, diff --git a/scripts/upgrades/upgrade-wearablesConfigFacet.ts b/scripts/upgrades/upgrade-wearablesConfigFacet.ts index 43e78f18..9cb41bb7 100644 --- a/scripts/upgrades/upgrade-wearablesConfigFacet.ts +++ b/scripts/upgrades/upgrade-wearablesConfigFacet.ts @@ -4,10 +4,7 @@ import { DeployUpgradeTaskArgs, FacetsAndAddSelectors, } from "../../tasks/deployUpgrade"; -import { - diamondOwner, - maticDiamondAddress, -} from "../helperFunctions"; +import { loadDeploymentConfig } from "../deployFullDiamond"; export async function upgrade() { const facets: FacetsAndAddSelectors[] = [ @@ -26,15 +23,14 @@ export async function upgrade() { }, ]; - const joined = convertFacetAndSelectorsToString(facets); + const deploymentConfig = loadDeploymentConfig(63157); + const diamondAddress = deploymentConfig.aavegotchiDiamond as string; + const diamondOwnerAddress = deploymentConfig.itemManagers[0] as string; - const diamondOwnerAddress = await diamondOwner(maticDiamondAddress, ethers) + const joined = convertFacetAndSelectorsToString(facets); - // todo: use deploymentConfig to detect args for Polter/Geist const args: DeployUpgradeTaskArgs = { - //diamondOwner: "0xd38Df837a1EAd12ee16f8b8b7E5F58703f841668", // polter-testnet - //diamondAddress: '0x226625C1B1174e7BaaE8cDC0432Db0e2ED83b7Ba', // polter-testnet - diamondAddress: maticDiamondAddress, + diamondAddress: diamondAddress, diamondOwner: diamondOwnerAddress, facetsAndAddSelectors: joined, useLedger: false, diff --git a/test/itemsFacetBatchEquip.ts b/test/itemsFacetBatchEquip.ts index fb9f0bbd..67cc768f 100644 --- a/test/itemsFacetBatchEquip.ts +++ b/test/itemsFacetBatchEquip.ts @@ -8,206 +8,327 @@ import { upgrade } from "../scripts/upgrades/upgrade-itemsFacet"; import { impersonate, resetChain } from "../scripts/helperFunctions"; import { AavegotchiFacet, + WearablesFacet, ItemsFacet, + ItemsRolesRegistryFacet, } from "../typechain"; +import { loadDeploymentConfig } from "../scripts/deployFullDiamond"; import * as helpers from "@nomicfoundation/hardhat-network-helpers"; -import { BigNumber, BigNumberish } from "ethers"; +import { + buildCommitment, + buildGrantRole, +} from "./ItemsRolesRegistryFacet/helpers"; const { expect } = chai; describe("Testing Batch Equip Wearables", async function () { this.timeout(300000); - const diamondAddress = "0x86935F11C86623deC8a25696E1C19a8659CbF95d"; // polygon mainnet - //const diamondAddress = '0x226625C1B1174e7BaaE8cDC0432Db0e2ED83b7Ba'; // polter-testnet - const wearables = [105, 209, 159, 104, 106, 65, 413, 210, 0, 0, 0, 0, 0, 0, 0, 0]; - const wearablesWithInvalidId = [418, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - const wearablesWithInvalidSlot = [104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - const wearablesWithRentals = [222, 146, 117, 147, 6, 148, 151, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]; - const depositIdsOfWearableRentals = [0, 384, 385, 1393, 380, 382, 383, 0, 0, 0, 0, 0, 0, 0, 0, 0] + const deploymentConfig = loadDeploymentConfig(63157); + const diamondAddress = deploymentConfig.aavegotchiDiamond as string; + const wearablesDiamondAddress = deploymentConfig.wearableDiamond as string; + const wearables = [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + const otherWearables = [0, 0, 0, 0, 205, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + const wearablesWithInvalidId = [ + 418, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]; + const wearablesWithInvalidSlot = [ + 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]; + const wearablesWithRentals = [ + 0, 0, 0, 1, 205, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]; + const depositIdsOfWearableRentals = [ + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]; const emptyWearables = new Array(16).fill(0); - const aavegotchiId = 4895; - const wearablesRentalAavegotchiId = 3166; + const aavegotchiId = 15748; + const anotherOwnerAavegotchiId = 19488; + const anotherWearablesOwner = "0xC3c2e1Cf099Bc6e1fA94ce358562BCbD5cc59FE5"; + const nullAddress = ethers.constants.AddressZero; let aavegotchiOwnerAddress: any; let anotherAavegotchiOwnerAddress: any; let gotchisOfOwner: number[]; let aavegotchiFacet: AavegotchiFacet; + let wearablesFacet: WearablesFacet; + let itemsRolesRegistryFacet: ItemsRolesRegistryFacet; + let aavegotchiFacetWithOwner: AavegotchiFacet; let itemsFacetWithOwner: ItemsFacet; + let wearablesFacetWithOwner: WearablesFacet; let itemsFacetWithOtherOwner: ItemsFacet; + let itemsRolesRegistryFacetWithOwner: Contract; before(async function () { - await resetChain(hre); + //await resetChain(hre); + // workaround for issue https://github.com/NomicFoundation/hardhat/issues/5511 - await helpers.mine() + //await helpers.mine() await upgrade(); aavegotchiFacet = (await ethers.getContractAt( "contracts/Aavegotchi/facets/AavegotchiFacet.sol:AavegotchiFacet", - diamondAddress + diamondAddress, )) as AavegotchiFacet; - const itemsFacet = (await ethers.getContractAt( + wearablesFacet = (await ethers.getContractAt( + "contracts/Aavegotchi/WearableDiamond/facets/WearablesFacet.sol:WearablesFacet", + wearablesDiamondAddress, + )) as WearablesFacet; + + itemsRolesRegistryFacet = await ethers.getContractAt( + "ItemsRolesRegistryFacet", + diamondAddress, + ); + + let itemsFacet = (await ethers.getContractAt( "contracts/Aavegotchi/facets/ItemsFacet.sol:ItemsFacet", - diamondAddress + diamondAddress, )) as ItemsFacet; aavegotchiOwnerAddress = await aavegotchiFacet.ownerOf(aavegotchiId); - anotherAavegotchiOwnerAddress = await aavegotchiFacet.ownerOf(wearablesRentalAavegotchiId); - gotchisOfOwner = await aavegotchiFacet.tokenIdsOfOwner(aavegotchiOwnerAddress); + anotherAavegotchiOwnerAddress = await aavegotchiFacet.ownerOf( + anotherOwnerAavegotchiId, + ); + + let aavegotchiFacetWithOtherSigner: AavegotchiFacet = await impersonate( + anotherAavegotchiOwnerAddress, + aavegotchiFacet, + ethers, + network, + ); + let wearablesFacetWithOtherSigner: WearablesFacet = await impersonate( + anotherWearablesOwner, + wearablesFacet, + ethers, + network, + ); + + await aavegotchiFacetWithOtherSigner.transferFrom( + anotherAavegotchiOwnerAddress, + aavegotchiOwnerAddress, + anotherOwnerAavegotchiId, + ); + await wearablesFacetWithOtherSigner.safeTransferFrom( + anotherWearablesOwner, + aavegotchiOwnerAddress, + 205, + 1, + "0x", + ); + + gotchisOfOwner = await aavegotchiFacet.tokenIdsOfOwner( + aavegotchiOwnerAddress, + ); + console.log("gotchisOfOwner", gotchisOfOwner); - //const accounts = await ethers.getSigners(); - //const ownerAddress = await accounts[0].getAddress(); + wearablesFacetWithOwner = await impersonate( + aavegotchiOwnerAddress, + wearablesFacet, + ethers, + network, + ); itemsFacetWithOwner = await impersonate( aavegotchiOwnerAddress, itemsFacet, ethers, - network + network, ); itemsFacetWithOtherOwner = await impersonate( anotherAavegotchiOwnerAddress, itemsFacet, ethers, - network + network, + ); + + aavegotchiFacetWithOwner = await impersonate( + aavegotchiOwnerAddress, + aavegotchiFacet, + ethers, + network, + ); + + itemsRolesRegistryFacetWithOwner = await impersonate( + aavegotchiOwnerAddress, + itemsRolesRegistryFacet, + ethers, + network, ); + + await itemsFacetWithOwner.equipWearables(gotchisOfOwner[1], otherWearables); }); describe("Testing batch functions to equip wearables", async function () { - - async function getWearables(_tokenId: number) : number[] { - const currentWearables = await itemsFacetWithOwner.equippedWearables(_tokenId); + async function getWearables(_tokenId: number): number[] { + const currentWearables = + await itemsFacetWithOwner.equippedWearables(_tokenId); return currentWearables; } it("Should unequip all wearables from multiple gotchis", async function () { await itemsFacetWithOwner.batchEquipWearables( - [ - gotchisOfOwner[3], - gotchisOfOwner[4], - gotchisOfOwner[5], - ], - [ - emptyWearables, - emptyWearables, - emptyWearables, - ] + [gotchisOfOwner[0], gotchisOfOwner[1]], + [emptyWearables, emptyWearables], + ); + expect(await getWearables(gotchisOfOwner[0])).to.deep.equal( + emptyWearables, + ); + expect(await getWearables(gotchisOfOwner[1])).to.deep.equal( + emptyWearables, + ); + + // reequip for next test + await itemsFacetWithOwner.equipWearables(gotchisOfOwner[0], wearables); + await itemsFacetWithOwner.equipWearables( + gotchisOfOwner[1], + otherWearables, ); - expect(await getWearables(gotchisOfOwner[3])).to.deep.equal(emptyWearables); - expect(await getWearables(gotchisOfOwner[4])).to.deep.equal(emptyWearables); - expect(await getWearables(gotchisOfOwner[5])).to.deep.equal(emptyWearables); }); it("Should be able to unequip and requip the same gotchi", async function () { await itemsFacetWithOwner.batchEquipWearables( - [ - aavegotchiId, - aavegotchiId, - ], - [ - emptyWearables, - wearables, - ] + [aavegotchiId, aavegotchiId], + [emptyWearables, wearables], ); expect(await getWearables(aavegotchiId)).to.deep.equal(wearables); }); it("Should be able to unequip from one gotchi to equip on the next", async function () { - // use H1 gotchis because of background await itemsFacetWithOwner.batchEquipWearables( - [ - gotchisOfOwner[2], - gotchisOfOwner[9], - ], - [ - emptyWearables, - wearables, - ] + [gotchisOfOwner[0], gotchisOfOwner[1]], + [emptyWearables, wearables], + ); + expect(await getWearables(gotchisOfOwner[0])).to.deep.equal( + emptyWearables, + ); + expect(await getWearables(gotchisOfOwner[1])).to.deep.equal(wearables); + + // reequip for next test + await itemsFacetWithOwner.equipWearables( + gotchisOfOwner[0], + otherWearables, ); - expect(await getWearables(gotchisOfOwner[2])).to.deep.equal(emptyWearables); - expect(await getWearables(gotchisOfOwner[9])).to.deep.equal(wearables); }); it("Should be able to completely flip wearables between two gotchis", async function () { - const firstGotchiId = gotchisOfOwner[13]; - const secondGotchiId = gotchisOfOwner[14]; - const firstGotchiWearables = await itemsFacetWithOwner.equippedWearables(firstGotchiId); - const secondGotchiWearables = await itemsFacetWithOwner.equippedWearables(secondGotchiId); + const firstGotchiId = gotchisOfOwner[0]; + const secondGotchiId = gotchisOfOwner[1]; + const firstGotchiWearables = + await itemsFacetWithOwner.equippedWearables(firstGotchiId); + const secondGotchiWearables = + await itemsFacetWithOwner.equippedWearables(secondGotchiId); await itemsFacetWithOwner.batchEquipWearables( - [ - firstGotchiId, - secondGotchiId, - firstGotchiId, - secondGotchiId, - ], + [firstGotchiId, secondGotchiId, firstGotchiId, secondGotchiId], [ emptyWearables, emptyWearables, secondGotchiWearables, firstGotchiWearables, - ] + ], + ); + expect(await getWearables(firstGotchiId)).to.deep.equal( + secondGotchiWearables, + ); + expect(await getWearables(secondGotchiId)).to.deep.equal( + firstGotchiWearables, + ); + + await itemsFacetWithOwner.equipWearables( + gotchisOfOwner[0], + emptyWearables, + ); + await aavegotchiFacetWithOwner.transferFrom( + aavegotchiOwnerAddress, + anotherAavegotchiOwnerAddress, + anotherOwnerAavegotchiId, ); - expect(await getWearables(firstGotchiId)).to.deep.equal(secondGotchiWearables); - expect(await getWearables(secondGotchiId)).to.deep.equal(firstGotchiWearables); }); it("Should unequip and requip wearables with rentals ", async function () { + let CommitmentCreated: Commitment; + let GrantRoleData: GrantRoleData; + let depositIdsCounter = 0; + + CommitmentCreated = buildCommitment({ + grantor: aavegotchiOwnerAddress, + tokenAddress: wearablesDiamondAddress, + tokenId: 1, + }); + + depositIdsCounter = Number( + ( + await itemsRolesRegistryFacet + .connect(aavegotchiOwnerAddress) + .callStatic.commitTokens( + CommitmentCreated.grantor, + CommitmentCreated.tokenAddress, + CommitmentCreated.tokenId, + CommitmentCreated.tokenAmount, + ) + ).toString(), + ); + console.log(depositIdsCounter); + + GrantRoleData = await buildGrantRole({ + depositId: depositIdsCounter, + grantee: anotherAavegotchiOwnerAddress, + }); + + await wearablesFacetWithOwner.setApprovalForAll( + itemsRolesRegistryFacet.address, + true, + ); + + await itemsRolesRegistryFacetWithOwner.commitTokens( + CommitmentCreated.grantor, + CommitmentCreated.tokenAddress, + CommitmentCreated.tokenId, + CommitmentCreated.tokenAmount, + ); + await itemsRolesRegistryFacetWithOwner.grantRole( + GrantRoleData.depositId, + GrantRoleData.role, + GrantRoleData.grantee, + GrantRoleData.expirationDate, + GrantRoleData.revocable, + GrantRoleData.data, + ); + await itemsFacetWithOtherOwner.batchEquipDelegatedWearables( - [ - wearablesRentalAavegotchiId, - wearablesRentalAavegotchiId, - ], - [ - emptyWearables, - wearablesWithRentals, - ], - [ - emptyWearables, - depositIdsOfWearableRentals, - ] + [anotherOwnerAavegotchiId, anotherOwnerAavegotchiId], + [emptyWearables, wearablesWithRentals], + [emptyWearables, depositIdsOfWearableRentals], + ); + expect(await getWearables(anotherOwnerAavegotchiId)).to.deep.equal( + wearablesWithRentals, ); - expect(await getWearables(wearablesRentalAavegotchiId)).to.deep.equal(wearablesWithRentals); }); it("Should revert if arguments are not all the same length ", async function () { await expect( itemsFacetWithOwner.batchEquipWearables( - [ - gotchisOfOwner[2], - gotchisOfOwner[9], - ], - [ - emptyWearables, - ] - ) - ).to.be.revertedWith("ItemsFacet: _wearablesToEquip length not same as _tokenIds length"); + [gotchisOfOwner[0], gotchisOfOwner[1]], + [emptyWearables], + ), + ).to.be.revertedWith( + "ItemsFacet: _wearablesToEquip length not same as _tokenIds length", + ); await expect( itemsFacetWithOtherOwner.batchEquipDelegatedWearables( - [ - wearablesRentalAavegotchiId, - wearablesRentalAavegotchiId, - ], - [ - emptyWearables, - ], - [ - emptyWearables, - emptyWearables, - ] - ) - ).to.be.revertedWith("ItemsFacet: _wearablesToEquip length not same as _tokenIds length"); + [anotherOwnerAavegotchiId, anotherOwnerAavegotchiId], + [emptyWearables], + [emptyWearables, emptyWearables], + ), + ).to.be.revertedWith( + "ItemsFacet: _wearablesToEquip length not same as _tokenIds length", + ); await expect( itemsFacetWithOtherOwner.batchEquipDelegatedWearables( - [ - wearablesRentalAavegotchiId, - wearablesRentalAavegotchiId, - ], - [ - emptyWearables, - emptyWearables, - ], - [ - emptyWearables, - ] - ) - ).to.be.revertedWith("ItemsFacet: _depositIds length not same as _tokenIds length"); + [aavegotchiId, aavegotchiId], + [emptyWearables, emptyWearables], + [emptyWearables], + ), + ).to.be.revertedWith( + "ItemsFacet: _depositIds length not same as _tokenIds length", + ); }); }); }); diff --git a/test/wearablesConfigTest.ts b/test/wearablesConfigTest.ts index 41a56fd2..7c936e0c 100644 --- a/test/wearablesConfigTest.ts +++ b/test/wearablesConfigTest.ts @@ -3,40 +3,49 @@ //@ts-ignore import { ethers, network } from "hardhat"; +import { BigNumber, BigNumberish } from "ethers"; import chai from "chai"; import { upgrade } from "../scripts/upgrades/upgrade-wearablesConfigFacet"; import { impersonate, resetChain } from "../scripts/helperFunctions"; import { AavegotchiFacet, WearablesConfigFacet, + GotchiLendingFacet, + LendingGetterAndSetterFacet, } from "../typechain"; import * as helpers from "@nomicfoundation/hardhat-network-helpers"; +import { loadDeploymentConfig } from "../scripts/deployFullDiamond"; const { expect } = chai; describe("Testing Wearables Config", async function () { this.timeout(300000); - const diamondAddress = "0x86935F11C86623deC8a25696E1C19a8659CbF95d"; // polygon mainnet - //const diamondAddress = '0x226625C1B1174e7BaaE8cDC0432Db0e2ED83b7Ba'; // polter-testnet + const deploymentConfig = loadDeploymentConfig(63157); + const diamondAddress = deploymentConfig.aavegotchiDiamond as string; + const diamondOwner = deploymentConfig.itemManagers[0] as string; + const slotPrice = ethers.utils.parseUnits("1", "ether"); const wearablesToStore = [105, 209, 159, 104, 106, 65, 413, 210, 0, 0, 0, 0, 0, 0, 0, 0]; const wearablesToStoreWithInvalidId = [418, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; const wearablesToStoreWithInvalidSlot = [104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - const aavegotchiId = 4895; - const rentedOutAavegotchiId = 9121; - const unsummonedAavegotchiId = 945; - const someoneElseAavegotchiId = 10356; + const aavegotchiId = 15748; + const unsummonedAavegotchiId = 1463; + const someoneElseAavegotchiId = 19488; let aavegotchiOwnerAddress: any; let anotherAavegotchiOwnerAddress: any; let daoAddress: any; let aavegotchiFacet: AavegotchiFacet; + let lendingGetterFacet: LendingGetterAndSetterFacet; + let aavegotchiFacetWithOwner: AavegotchiFacet; let wearablesConfigFacetWithOwner: WearablesConfigFacet; let wearablesConfigFacetWithOtherOwner: WearablesConfigFacet; + let gotchiLendingFacetWithOwner: GotchiLendingFacet; + let gotchiLendingFacetWithOtherOwner: GotchiLendingFacet; before(async function () { - await resetChain(hre); + //await resetChain(hre); // workaround for issue https://github.com/NomicFoundation/hardhat/issues/5511 await helpers.mine() @@ -47,20 +56,36 @@ describe("Testing Wearables Config", async function () { diamondAddress )) as AavegotchiFacet; - //aavegotchiOwnerAddress = await aavegotchiFacet.ownerOf(aavegotchiId); - aavegotchiOwnerAddress = "0x43FF4C088df0A425d1a519D3030A1a3DFff05CfD"; - anotherAavegotchiOwnerAddress = "0xE1bCD0f5c6c855ee3452B38E16FeD0b7Cb0CC507"; + lendingGetterFacet = await ethers.getContractAt( + "LendingGetterAndSetterFacet", + diamondAddress + ); + + aavegotchiOwnerAddress = await aavegotchiFacet.ownerOf(aavegotchiId); + anotherAavegotchiOwnerAddress = "0xC3c2e1Cf099Bc6e1fA94ce358562BCbD5cc59FE5"; //const accounts = await ethers.getSigners(); //const ownerAddress = await accounts[0].getAddress(); //daoAddress = ownerAddress; - daoAddress = "0xb208f8BB431f580CC4b216826AFfB128cd1431aB"; + daoAddress = diamondOwner; // Geist fresh deployment + + const gotchiLendingFacet = (await ethers.getContractAt( + "GotchiLendingFacet", + diamondAddress + )) as GotchiLendingFacet; const wearablesConfigFacet = (await ethers.getContractAt( "WearablesConfigFacet", diamondAddress )) as WearablesConfigFacet; + aavegotchiFacetWithOwner = await impersonate( + anotherAavegotchiOwnerAddress, + aavegotchiFacet, + ethers, + network + ); + wearablesConfigFacetWithOwner = await impersonate( aavegotchiOwnerAddress, wearablesConfigFacet, @@ -74,6 +99,23 @@ describe("Testing Wearables Config", async function () { ethers, network ); + + gotchiLendingFacetWithOwner = await impersonate( + aavegotchiOwnerAddress, + gotchiLendingFacet, + ethers, + network + ); + + gotchiLendingFacetWithOtherOwner = await impersonate( + anotherAavegotchiOwnerAddress, + gotchiLendingFacet, + ethers, + network + ); + + await aavegotchiFacetWithOwner.transferFrom(anotherAavegotchiOwnerAddress, aavegotchiOwnerAddress, unsummonedAavegotchiId); + }); describe("Testing createWearablesConfig", async function () { @@ -284,30 +326,68 @@ describe("Testing Wearables Config", async function () { ).to.equal("Test 4th"); }); }); - describe("Testing for rented gotchis", async function () { - it("Should be able to save for a rented gotchi", async function () { - // config #5 (id: 4) - await wearablesConfigFacetWithOtherOwner.createWearablesConfig(rentedOutAavegotchiId, "Test Rental", wearablesToStore, { value: ethers.utils.parseEther("1") }) - expect( - await wearablesConfigFacetWithOwner.getWearablesConfigName(anotherAavegotchiOwnerAddress, rentedOutAavegotchiId, 0) - ).to.equal("Test Rental"); - }); - it("Should be able to update for a rented gotchi", async function () { - const newWearablesToStore = new Array(16).fill(0); - await wearablesConfigFacetWithOtherOwner.updateWearablesConfig(rentedOutAavegotchiId, 0, "Test Update Rental", newWearablesToStore) - expect( - await wearablesConfigFacetWithOwner.getWearablesConfigName(anotherAavegotchiOwnerAddress, rentedOutAavegotchiId, 0) - ).to.equal("Test Update Rental"); - expect( - await wearablesConfigFacetWithOwner.getWearablesConfigWearables(anotherAavegotchiOwnerAddress, rentedOutAavegotchiId, 0) - ).to.eql(newWearablesToStore); - }); - it("Should revert on create for rented out gotchi", async function () { - await expect( - wearablesConfigFacetWithOwner.createWearablesConfig(rentedOutAavegotchiId, "Test Rented Out", wearablesToStore, { value: ethers.utils.parseEther("1") }) - ).to.be.revertedWith("LibAppStorage: Only aavegotchi owner can call this function"); - }); - }); + //describe("Testing for rented gotchis", async function () { + // + // it("Should be able to save for a rented gotchi", async function () { + // + // // create a listing rent a gotchi + // const revenueSplit = [100, 0, 0] as [ + // BigNumberish, + // BigNumberish, + // BigNumberish + // ]; + // await gotchiLendingFacetWithOwner.addGotchiListing({ + // tokenId: aavegotchiId, + // initialCost: 0, + // period: 86400, + // revenueSplit: revenueSplit, + // originalOwner: aavegotchiOwnerAddress, + // thirdParty: ethers.constants.AddressZero, + // whitelistId: 0, + // revenueTokens: [], + // permissions: 0, + // }); + // + // const listing = await lendingGetterFacet.getGotchiLendingFromToken( + // aavegotchiId + // ); + // console.log(listing); + // + // await gotchiLendingFacetWithOtherOwner.agreeGotchiLending( + // listing.listingId, + // aavegotchiId, + // 0, + // 86400, + // revenueSplit, + // ); + // + // const lending = await lendingGetterFacet.getGotchiLendingFromToken( + // aavegotchiId + // ); + // expect(lending.borrower).to.equal(anotherAavegotchiOwnerAddress); + // + // // config #5 (id: 4) + // await wearablesConfigFacetWithOtherOwner.createWearablesConfig(aavegotchiId, "Test Rental", wearablesToStore, { value: ethers.utils.parseEther("1") }) + // expect( + // await wearablesConfigFacetWithOwner.getWearablesConfigName(anotherAavegotchiOwnerAddress, aavegotchiId, 0) + // ).to.equal("Test Rental"); + // }); + // it("Should be able to update for a rented gotchi", async function () { + // const newWearablesToStore = new Array(16).fill(0); + // await wearablesConfigFacetWithOtherOwner.updateWearablesConfig(aavegotchiId, 0, "Test Update Rental", newWearablesToStore) + // expect( + // await wearablesConfigFacetWithOwner.getWearablesConfigName(anotherAavegotchiOwnerAddress, aavegotchiId, 0) + // ).to.equal("Test Update Rental"); + // expect( + // await wearablesConfigFacetWithOwner.getWearablesConfigWearables(anotherAavegotchiOwnerAddress, aavegotchiId, 0) + // ).to.eql(newWearablesToStore); + // }); + // it("Should revert on create for rented out gotchi", async function () { + // await expect( + // wearablesConfigFacetWithOwner.createWearablesConfig(aavegotchiId, "Test Rented Out", wearablesToStore, { value: ethers.utils.parseEther("1") }) + // ).to.be.revertedWith("LibAppStorage: Only aavegotchi owner can call this function"); + // }); + //}); describe("Testing for invalid gotchis", async function () { it("Should revert for a portal", async function () { await expect( @@ -347,18 +427,18 @@ describe("Testing Wearables Config", async function () { ).to.be.revertedWith("WearablesConfigFacet: Invalid wearables"); }); }); - describe("Testing for max wearable configs", async function () { - it("Shouldn't be able to create more than 255", async function () { - for (let i = 4; i < 255; i++) { - // config #5-255 (id: 4-254) - await wearablesConfigFacetWithOwner.createWearablesConfig(aavegotchiId, "Test", wearablesToStore, { value: ethers.utils.parseEther("1") }); - } - expect( - await wearablesConfigFacetWithOwner.getAavegotchiWearablesConfigCount(aavegotchiOwnerAddress, aavegotchiId) - ).to.equal(255); - await expect( - wearablesConfigFacetWithOwner.createWearablesConfig(aavegotchiId, "Test 256th", wearablesToStore, { value: ethers.utils.parseEther("1") }) - ).to.be.revertedWith("WearablesConfigFacet: No more wearables config slots available"); - }); - }); + //describe("Testing for max wearable configs", async function () { + // it("Shouldn't be able to create more than 255", async function () { + // for (let i = 4; i < 255; i++) { + // // config #5-255 (id: 4-254) + // await wearablesConfigFacetWithOwner.createWearablesConfig(aavegotchiId, "Test", wearablesToStore, { value: ethers.utils.parseEther("1") }); + // } + // expect( + // await wearablesConfigFacetWithOwner.getAavegotchiWearablesConfigCount(aavegotchiOwnerAddress, aavegotchiId) + // ).to.equal(255); + // await expect( + // wearablesConfigFacetWithOwner.createWearablesConfig(aavegotchiId, "Test 256th", wearablesToStore, { value: ethers.utils.parseEther("1") }) + // ).to.be.revertedWith("WearablesConfigFacet: No more wearables config slots available"); + // }); + //}); }); From bca8e625a72f2cae6612773cbab96ca057a9f316 Mon Sep 17 00:00:00 2001 From: pgendreau Date: Sat, 18 Jan 2025 00:03:34 -0500 Subject: [PATCH 6/8] AGIP135: add not owned and unbridged gotchis to WearablesConfig --- .../facets/WearablesConfigFacet.sol | 135 +++-- .../libraries/LibWearablesConfig.sol | 24 +- test/itemsFacetBatchEquip.ts | 61 +- test/wearablesConfigTest.ts | 573 ++++++++++++++---- 4 files changed, 597 insertions(+), 196 deletions(-) diff --git a/contracts/Aavegotchi/facets/WearablesConfigFacet.sol b/contracts/Aavegotchi/facets/WearablesConfigFacet.sol index 7f4599be..312c854e 100644 --- a/contracts/Aavegotchi/facets/WearablesConfigFacet.sol +++ b/contracts/Aavegotchi/facets/WearablesConfigFacet.sol @@ -8,28 +8,26 @@ import {LibWearablesConfig} from "../libraries/LibWearablesConfig.sol"; contract WearablesConfigFacet is Modifiers { - /** - * @dev The number of free slots available for wearables configurations. - */ - uint8 constant WEARABLESCONFIG_FREE_SLOTS = 3; - /** - * @dev The price to create a wearable config slot once the free slots have been reached. - */ - uint256 constant WEARABLESCONFIG_SLOT_PRICE = 1000000000000000000; // 1 GHST - - uint256 constant MAX_UINT8 = 2**8 - 1; + // constants + uint256 private constant MAX_UINT8 = 2**8 - 1; + uint8 public constant WEARABLESCONFIG_FREE_SLOTS = 3; + uint256 public constant WEARABLESCONFIG_SLOT_PRICE = 1000000000000000000; // 1 GHST + uint256 public constant WEARABLESCONFIG_OWNER_FEE = 100000000000000000; // 0.1 GHST + // events event WearablesConfigCreated(address indexed owner, uint256 indexed tokenId, uint8 indexed wearablesConfigId, uint16[EQUIPPED_WEARABLE_SLOTS] wearables, uint256 value); event WearablesConfigUpdated(address indexed owner, uint256 indexed tokenId, uint8 indexed wearablesConfigId, uint16[EQUIPPED_WEARABLE_SLOTS] wearables); - event WearablesConfigPaymentReceived(address indexed owner, uint256 indexed tokenId, uint8 indexed wearablesConfigId, uint256 value); - - /** - * @notice Creates and stores a new wearables configuration (max 255 per Aavegotchi per owner). - * @param _tokenId The ID of the aavegotchi to create the wearables configuration for. - * @param _name The name of the wearables configuration. - * @param _wearablesToStore The wearables to store for this wearables configuration. - * @return wearablesConfigId The ID of the newly created wearables configuration. - */ + event WearablesConfigDaoPaymentReceived(address indexed owner, uint256 indexed tokenId, uint8 indexed wearablesConfigId, uint256 value); + event WearablesConfigOwnerPaymentReceived(address indexed sender, address indexed owner, uint256 indexed tokenId, uint8 wearablesConfigId, uint256 value); + + /// @notice Creates and stores a new wearables configuration (max 255 per Aavegotchi per owner). + /// @notice First three slots are free, the rest are paid. + /// @notice To create a wearables config for someone else aavegotchi there is a fee + /// @notice We support wearables config creation for unbridged gotchis (config owner is set to sender) + /// @param _tokenId The ID of the aavegotchi to create the wearables configuration for. + /// @param _name The name of the wearables configuration. + /// @param _wearablesToStore The wearables to store for this wearables configuration. + /// @return wearablesConfigId The ID of the newly created wearables configuration. function createWearablesConfig( uint256 _tokenId, string calldata _name, @@ -37,42 +35,70 @@ contract WearablesConfigFacet is Modifiers { ) external payable - onlyAavegotchiOwner(_tokenId) returns (uint8 wearablesConfigId) { - // check that the aavegotchi has been summoned - require(s.aavegotchis[_tokenId].status == LibAavegotchi.STATUS_AAVEGOTCHI, "WearablesConfigFacet: Can only create wearables config for Aavegotchi"); + // check that creation of this wearables config is allowed (only aavegotchi or unbridged) + require(LibWearablesConfig._checkAavegotchiOrUnbridged(_tokenId), "WearablesConfigFacet: Not allowed to create wearables config"); // check that wearables are valid and for the right slots require(LibWearablesConfig._checkValidWearables(_wearablesToStore), "WearablesConfigFacet: Invalid wearables"); // check that name is not empty require(bytes(_name).length > 0, "WearablesConfigFacet: WearablesConfig name cannot be blank"); - address owner = LibMeta.msgSender(); + address sender = LibMeta.msgSender(); + address owner = s.aavegotchis[_tokenId].owner; + bool paidslot; + bool notowner; + uint256 fee; + if (owner == address(0)) { + // set the owner to the sender for unbridged gotchis + owner = sender; + } + // get the next available slot - uint8 newWearablesConfigId = LibWearablesConfig._getNextWearablesConfigId(owner, _tokenId); + wearablesConfigId = LibWearablesConfig._getNextWearablesConfigId(owner, _tokenId); // solidity will throw if slots used overflows - require(newWearablesConfigId < 2**8 - 1, "WearablesConfigFacet: No more wearables config slots available"); + require(wearablesConfigId < 2**8 - 1, "WearablesConfigFacet: No more wearables config slots available"); - // create the new wearables config and add it to the gotchi for that owner - WearablesConfig memory newWearablesConfig = WearablesConfig({name: _name, wearables: _wearablesToStore}); - s.gotchiWearableConfigs[_tokenId][owner].push(newWearablesConfig); - s.ownerGotchiSlotsUsed[owner][_tokenId] += 1; + // if the owner has reached the free slots limit then they need to pay for the extra slot + if (wearablesConfigId >= WEARABLESCONFIG_FREE_SLOTS) { + paidslot = true; + fee += WEARABLESCONFIG_SLOT_PRICE; + } + + // if the sender is not the owner and the gotchi has been bridged + // then they need to pay a fee to the owner + if (sender != owner) { + notowner = true; + fee += WEARABLESCONFIG_OWNER_FEE; + } + + if (fee > 0) { + require(msg.value == fee, "WearablesConfigFacet: Incorrect GHST value sent"); + + if (paidslot) { + // send GHST to the dao treasury + (bool success, ) = payable(s.daoTreasury).call{value: WEARABLESCONFIG_SLOT_PRICE}(""); + require(success, "WearablesConfigFacet: Failed to send GHST to DAO treasury"); - // if the ownner has reached the free slots limit then they need to pay for the extra slot - if (newWearablesConfigId >= WEARABLESCONFIG_FREE_SLOTS) { - require(msg.value == WEARABLESCONFIG_SLOT_PRICE, "WearablesConfigFacet: Incorrect GHST value sent"); + emit WearablesConfigDaoPaymentReceived(owner, _tokenId, wearablesConfigId, WEARABLESCONFIG_SLOT_PRICE); + } - // send GHST to the dao treasury - (bool success, ) = payable(s.daoTreasury).call{value: msg.value}(""); - require(success, "WearablesConfigFacet: Failed to send GHST to DAO treasury"); + if (notowner) { + // send GHST to the owner + (bool success, ) = payable(owner).call{value: WEARABLESCONFIG_OWNER_FEE}(""); + require(success, "WearablesConfigFacet: Failed to send GHST to owner"); - emit WearablesConfigPaymentReceived(owner, _tokenId, newWearablesConfigId, msg.value); + emit WearablesConfigOwnerPaymentReceived(sender, owner, _tokenId, wearablesConfigId, WEARABLESCONFIG_OWNER_FEE); + } } - emit WearablesConfigCreated(owner, _tokenId, newWearablesConfigId, _wearablesToStore, msg.value); + // create the new wearables config and add it to the gotchi for that owner + WearablesConfig memory wearablesConfig = WearablesConfig({name: _name, wearables: _wearablesToStore}); + s.gotchiWearableConfigs[_tokenId][owner].push(wearablesConfig); + s.ownerGotchiSlotsUsed[owner][_tokenId] += 1; - return newWearablesConfigId; + emit WearablesConfigCreated(owner, _tokenId, wearablesConfigId, _wearablesToStore, msg.value); } /// @notice Updates the wearables config for the given wearables config id @@ -87,14 +113,33 @@ contract WearablesConfigFacet is Modifiers { string calldata _name, uint16[EQUIPPED_WEARABLE_SLOTS] calldata _wearablesToStore ) - external - onlyAavegotchiOwner(_tokenId) + external payable { - address owner = LibMeta.msgSender(); + address sender = LibMeta.msgSender(); + address owner = s.aavegotchis[_tokenId].owner; + // check that update of this wearables config is allowed (only aavegotchi or unbridged) + require(LibWearablesConfig._checkAavegotchiOrUnbridged(_tokenId), "WearablesConfigFacet: Not allowed to update wearables config"); + // check that wearables are valid and for the right slots require(LibWearablesConfig._checkValidWearables(_wearablesToStore), "WearablesConfigFacet: Invalid wearables"); + + if (owner == address(0)) { + // set the owner to the sender for unbridged gotchis + owner = sender; + } + + // make sure we are updating an existing wearables config require(LibWearablesConfig._wearablesConfigExists(owner, _tokenId, _wearablesConfigId), "WearablesConfigFacet: invalid id, WearablesConfig not found"); + // if the sender is not the owner and the gotchi has been bridged + // then they need to pay a fee to the owner + if (sender != owner) { + (bool success, ) = payable(owner).call{value: WEARABLESCONFIG_OWNER_FEE}(""); + require(success, "WearablesConfigFacet: Failed to send GHST to owner"); + + emit WearablesConfigOwnerPaymentReceived(sender, owner, _tokenId, _wearablesConfigId, WEARABLESCONFIG_OWNER_FEE); + } + // skip if name is empty if (bytes(_name).length > 0) { s.gotchiWearableConfigs[_tokenId][owner][_wearablesConfigId].name = _name; @@ -132,7 +177,7 @@ contract WearablesConfigFacet is Modifiers { /// @return name The name of the wearables config function getWearablesConfigName(address _owner, uint256 _tokenId, uint8 _wearablesConfigId) external view returns (string memory name) { require(LibWearablesConfig._wearablesConfigExists(_owner, _tokenId, _wearablesConfigId), "WearablesConfigFacet: invalid id, WearablesConfig not found"); - return s.gotchiWearableConfigs[_tokenId][_owner][_wearablesConfigId].name; + name = s.gotchiWearableConfigs[_tokenId][_owner][_wearablesConfigId].name; } /// @notice Returns the wearables for the given wearables config id @@ -142,14 +187,14 @@ contract WearablesConfigFacet is Modifiers { /// @return wearables The wearables stored for this wearables config function getWearablesConfigWearables(address _owner, uint256 _tokenId, uint8 _wearablesConfigId) external view returns (uint16[EQUIPPED_WEARABLE_SLOTS] memory wearables) { require(LibWearablesConfig._wearablesConfigExists(_owner, _tokenId, _wearablesConfigId), "WearablesConfigFacet: invalid id, WearablesConfig not found"); - return s.gotchiWearableConfigs[_tokenId][_owner][_wearablesConfigId].wearables; + wearables = s.gotchiWearableConfigs[_tokenId][_owner][_wearablesConfigId].wearables; } /// @notice Returns the number of wearables configs for the given aavegotchi for this owner /// @param _owner The owner of the aavegotchi /// @param _tokenId The ID of the aavegotchi to update the wearables configuration for /// @return slotsUsed The number of wearables configs - function getAavegotchiWearablesConfigCount(address _owner, uint256 _tokenId) external view returns (uint256 slotsUsed) { - return s.ownerGotchiSlotsUsed[_owner][_tokenId]; + function getAavegotchiWearablesConfigCount(address _owner, uint256 _tokenId) external view returns (uint8 slotsUsed) { + slotsUsed = s.ownerGotchiSlotsUsed[_owner][_tokenId]; } } diff --git a/contracts/Aavegotchi/libraries/LibWearablesConfig.sol b/contracts/Aavegotchi/libraries/LibWearablesConfig.sol index c4912e4f..fc39f47d 100644 --- a/contracts/Aavegotchi/libraries/LibWearablesConfig.sol +++ b/contracts/Aavegotchi/libraries/LibWearablesConfig.sol @@ -2,11 +2,32 @@ pragma solidity 0.8.1; import {LibMeta} from "../../shared/libraries/LibMeta.sol"; +import {LibAavegotchi} from "../libraries/LibAavegotchi.sol"; import {LibItems} from "../libraries/LibItems.sol"; import {LibAppStorage, AppStorage, WearablesConfig, ItemType, EQUIPPED_WEARABLE_SLOTS} from "../libraries/LibAppStorage.sol"; library LibWearablesConfig { + /// @notice Returns true only if the given tokenId is a valid aavegotchi or unbridged + /// @param _tokenId The tokenId of the aavegotchi + /// @return result True if the tokenId is valid or unbridged + function _checkAavegotchiOrUnbridged(uint256 _tokenId) internal view returns (bool result) { + AppStorage storage s = LibAppStorage.diamondStorage(); + if (s.aavegotchis[_tokenId].status == LibAavegotchi.STATUS_AAVEGOTCHI) { + result = true; + // Unbridged aavegotchis do not have a owner or a haunt set + } else if (s.aavegotchis[_tokenId].hauntId == 0 && s.aavegotchis[_tokenId].owner == address(0)) { + // Only allow unbridged aavegotchis up to the current supply + uint256 maxSupply; + for (uint256 i = 1; i <= s.currentHauntId; i++) { + maxSupply += s.haunts[i].hauntMaxSize; + } + require(_tokenId < maxSupply, "LibWearablesConfig: Invalid tokenId for unbridged aavegotchi"); + + result = true; + } + } + /// @notice Returns the next wearables config id for that gotchi given that owner /// @param _owner The owner of the gotchi /// @param _tokenId The tokenId of the gotchi @@ -17,7 +38,6 @@ library LibWearablesConfig { nextWearablesConfigId = s.ownerGotchiSlotsUsed[_owner][_tokenId]; } - /// @notice Checks if a wearables config exists for a gotchi given an owner /// @param _owner The owner of the gotchi /// @param _tokenId The tokenId of the gotchi @@ -32,7 +52,7 @@ library LibWearablesConfig { /// @notice Checks if a wearables configuration consist of valid wearables and are for the correct slot /// @param _wearablesToStore The wearables to store /// @return valid True if the wearables configuration is valid and false otherwise - function _checkValidWearables(uint16[EQUIPPED_WEARABLE_SLOTS] memory _wearablesToStore) internal view returns (bool valid) { + function _checkValidWearables(uint16[EQUIPPED_WEARABLE_SLOTS] memory _wearablesToStore) internal view returns (bool) { AppStorage storage s = LibAppStorage.diamondStorage(); uint256 itemTypesLength = s.itemTypes.length; bool valid = true; diff --git a/test/itemsFacetBatchEquip.ts b/test/itemsFacetBatchEquip.ts index 67cc768f..e607daf4 100644 --- a/test/itemsFacetBatchEquip.ts +++ b/test/itemsFacetBatchEquip.ts @@ -87,11 +87,13 @@ describe("Testing Batch Equip Wearables", async function () { diamondAddress, )) as ItemsFacet; + // get owners of gotchis aavegotchiOwnerAddress = await aavegotchiFacet.ownerOf(aavegotchiId); anotherAavegotchiOwnerAddress = await aavegotchiFacet.ownerOf( anotherOwnerAavegotchiId, ); + // impersonate signers let aavegotchiFacetWithOtherSigner: AavegotchiFacet = await impersonate( anotherAavegotchiOwnerAddress, aavegotchiFacet, @@ -104,53 +106,30 @@ describe("Testing Batch Equip Wearables", async function () { ethers, network, ); - - await aavegotchiFacetWithOtherSigner.transferFrom( - anotherAavegotchiOwnerAddress, - aavegotchiOwnerAddress, - anotherOwnerAavegotchiId, - ); - await wearablesFacetWithOtherSigner.safeTransferFrom( - anotherWearablesOwner, - aavegotchiOwnerAddress, - 205, - 1, - "0x", - ); - - gotchisOfOwner = await aavegotchiFacet.tokenIdsOfOwner( - aavegotchiOwnerAddress, - ); - console.log("gotchisOfOwner", gotchisOfOwner); - wearablesFacetWithOwner = await impersonate( aavegotchiOwnerAddress, wearablesFacet, ethers, network, ); - itemsFacetWithOwner = await impersonate( aavegotchiOwnerAddress, itemsFacet, ethers, network, ); - itemsFacetWithOtherOwner = await impersonate( anotherAavegotchiOwnerAddress, itemsFacet, ethers, network, ); - aavegotchiFacetWithOwner = await impersonate( aavegotchiOwnerAddress, aavegotchiFacet, ethers, network, ); - itemsRolesRegistryFacetWithOwner = await impersonate( aavegotchiOwnerAddress, itemsRolesRegistryFacet, @@ -158,6 +137,26 @@ describe("Testing Batch Equip Wearables", async function () { network, ); + // transfer gotchi and wearable for tests + await aavegotchiFacetWithOtherSigner.transferFrom( + anotherAavegotchiOwnerAddress, + aavegotchiOwnerAddress, + anotherOwnerAavegotchiId, + ); + await wearablesFacetWithOtherSigner.safeTransferFrom( + anotherWearablesOwner, + aavegotchiOwnerAddress, + 205, + 1, + "0x", + ); + + // get gotchis of owner + gotchisOfOwner = await aavegotchiFacet.tokenIdsOfOwner( + aavegotchiOwnerAddress, + ); + + // equip transfered wearable on transeferd gotchi await itemsFacetWithOwner.equipWearables(gotchisOfOwner[1], otherWearables); }); @@ -180,7 +179,7 @@ describe("Testing Batch Equip Wearables", async function () { emptyWearables, ); - // reequip for next test + // reset for next test await itemsFacetWithOwner.equipWearables(gotchisOfOwner[0], wearables); await itemsFacetWithOwner.equipWearables( gotchisOfOwner[1], @@ -204,7 +203,7 @@ describe("Testing Batch Equip Wearables", async function () { ); expect(await getWearables(gotchisOfOwner[1])).to.deep.equal(wearables); - // reequip for next test + // reequip for next test (inversed) await itemsFacetWithOwner.equipWearables( gotchisOfOwner[0], otherWearables, @@ -213,10 +212,8 @@ describe("Testing Batch Equip Wearables", async function () { it("Should be able to completely flip wearables between two gotchis", async function () { const firstGotchiId = gotchisOfOwner[0]; const secondGotchiId = gotchisOfOwner[1]; - const firstGotchiWearables = - await itemsFacetWithOwner.equippedWearables(firstGotchiId); - const secondGotchiWearables = - await itemsFacetWithOwner.equippedWearables(secondGotchiId); + const firstGotchiWearables = await getWearables(firstGotchiId); + const secondGotchiWearables = await getWearables(secondGotchiId); await itemsFacetWithOwner.batchEquipWearables( [firstGotchiId, secondGotchiId, firstGotchiId, secondGotchiId], [ @@ -233,10 +230,13 @@ describe("Testing Batch Equip Wearables", async function () { firstGotchiWearables, ); + // unequip to free wearables for next test await itemsFacetWithOwner.equipWearables( gotchisOfOwner[0], emptyWearables, ); + + // retransfer to other owner for next test await aavegotchiFacetWithOwner.transferFrom( aavegotchiOwnerAddress, anotherAavegotchiOwnerAddress, @@ -248,6 +248,7 @@ describe("Testing Batch Equip Wearables", async function () { let GrantRoleData: GrantRoleData; let depositIdsCounter = 0; + // rent a wearable CommitmentCreated = buildCommitment({ grantor: aavegotchiOwnerAddress, tokenAddress: wearablesDiamondAddress, @@ -266,7 +267,6 @@ describe("Testing Batch Equip Wearables", async function () { ) ).toString(), ); - console.log(depositIdsCounter); GrantRoleData = await buildGrantRole({ depositId: depositIdsCounter, @@ -293,6 +293,7 @@ describe("Testing Batch Equip Wearables", async function () { GrantRoleData.data, ); + // unequip and reequip with rental await itemsFacetWithOtherOwner.batchEquipDelegatedWearables( [anotherOwnerAavegotchiId, anotherOwnerAavegotchiId], [emptyWearables, wearablesWithRentals], diff --git a/test/wearablesConfigTest.ts b/test/wearablesConfigTest.ts index 7c936e0c..70b75c45 100644 --- a/test/wearablesConfigTest.ts +++ b/test/wearablesConfigTest.ts @@ -26,9 +26,15 @@ describe("Testing Wearables Config", async function () { const diamondOwner = deploymentConfig.itemManagers[0] as string; const slotPrice = ethers.utils.parseUnits("1", "ether"); - const wearablesToStore = [105, 209, 159, 104, 106, 65, 413, 210, 0, 0, 0, 0, 0, 0, 0, 0]; - const wearablesToStoreWithInvalidId = [418, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - const wearablesToStoreWithInvalidSlot = [104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + const wearablesToStore = [ + 105, 209, 159, 104, 106, 65, 413, 210, 0, 0, 0, 0, 0, 0, 0, 0, + ]; + const wearablesToStoreWithInvalidId = [ + 418, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]; + const wearablesToStoreWithInvalidSlot = [ + 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]; const aavegotchiId = 15748; const unsummonedAavegotchiId = 1463; const someoneElseAavegotchiId = 19488; @@ -47,22 +53,23 @@ describe("Testing Wearables Config", async function () { before(async function () { //await resetChain(hre); // workaround for issue https://github.com/NomicFoundation/hardhat/issues/5511 - await helpers.mine() + await helpers.mine(); await upgrade(); aavegotchiFacet = (await ethers.getContractAt( "contracts/Aavegotchi/facets/AavegotchiFacet.sol:AavegotchiFacet", - diamondAddress + diamondAddress, )) as AavegotchiFacet; lendingGetterFacet = await ethers.getContractAt( "LendingGetterAndSetterFacet", - diamondAddress + diamondAddress, ); aavegotchiOwnerAddress = await aavegotchiFacet.ownerOf(aavegotchiId); - anotherAavegotchiOwnerAddress = "0xC3c2e1Cf099Bc6e1fA94ce358562BCbD5cc59FE5"; + anotherAavegotchiOwnerAddress = + "0xC3c2e1Cf099Bc6e1fA94ce358562BCbD5cc59FE5"; //const accounts = await ethers.getSigners(); //const ownerAddress = await accounts[0].getAddress(); @@ -71,75 +78,90 @@ describe("Testing Wearables Config", async function () { const gotchiLendingFacet = (await ethers.getContractAt( "GotchiLendingFacet", - diamondAddress + diamondAddress, )) as GotchiLendingFacet; const wearablesConfigFacet = (await ethers.getContractAt( "WearablesConfigFacet", - diamondAddress + diamondAddress, )) as WearablesConfigFacet; aavegotchiFacetWithOwner = await impersonate( anotherAavegotchiOwnerAddress, aavegotchiFacet, ethers, - network + network, ); wearablesConfigFacetWithOwner = await impersonate( aavegotchiOwnerAddress, wearablesConfigFacet, ethers, - network + network, ); wearablesConfigFacetWithOtherOwner = await impersonate( anotherAavegotchiOwnerAddress, wearablesConfigFacet, ethers, - network + network, ); gotchiLendingFacetWithOwner = await impersonate( aavegotchiOwnerAddress, gotchiLendingFacet, ethers, - network + network, ); gotchiLendingFacetWithOtherOwner = await impersonate( anotherAavegotchiOwnerAddress, gotchiLendingFacet, ethers, - network + network, ); - await aavegotchiFacetWithOwner.transferFrom(anotherAavegotchiOwnerAddress, aavegotchiOwnerAddress, unsummonedAavegotchiId); - + await aavegotchiFacetWithOwner.transferFrom( + anotherAavegotchiOwnerAddress, + aavegotchiOwnerAddress, + unsummonedAavegotchiId, + ); }); describe("Testing createWearablesConfig", async function () { it("Should revert if wearables list is invalid", async function () { const invalidWearables = new Array(16).fill(1); await expect( - wearablesConfigFacetWithOwner.createWearablesConfig(aavegotchiId, "Test", invalidWearables) - ).to.be.revertedWith( - "WearablesConfigFacet: Invalid wearables" - ); + wearablesConfigFacetWithOwner.createWearablesConfig( + aavegotchiId, + "Test", + invalidWearables, + ), + ).to.be.revertedWith("WearablesConfigFacet: Invalid wearables"); }); it("Should revert if wearablesConfig name is empty", async function () { await expect( - wearablesConfigFacetWithOwner.createWearablesConfig(aavegotchiId, "", wearablesToStore) - ).to.be.revertedWith("WearablesConfigFacet: WearablesConfig name cannot be blank"); + wearablesConfigFacetWithOwner.createWearablesConfig( + aavegotchiId, + "", + wearablesToStore, + ), + ).to.be.revertedWith( + "WearablesConfigFacet: WearablesConfig name cannot be blank", + ); }); it("Should succeed to create wearablesConfig if all parameters are valid and emit event", async function () { - const receipt = await ( - // config #1 (id: 0) - await wearablesConfigFacetWithOwner.createWearablesConfig(aavegotchiId, "Test", wearablesToStore) + const receipt = await // config #1 (id: 0) + ( + await wearablesConfigFacetWithOwner.createWearablesConfig( + aavegotchiId, + "Test", + wearablesToStore, + ) ).wait(); // check that event is emitted with the right parameters const event = receipt!.events!.find( - (event) => event.event === "WearablesConfigCreated" + (event) => event.event === "WearablesConfigCreated", ); const owner = event!.args!.owner; const tokenId = event!.args!.tokenId; @@ -157,52 +179,101 @@ describe("Testing Wearables Config", async function () { describe("Testing getWearablesConfigName", async function () { it("Should revert if invalid wearablesConfig id", async function () { await expect( - wearablesConfigFacetWithOwner.getWearablesConfigName(aavegotchiOwnerAddress, aavegotchiId, 99) - ).to.be.revertedWith("WearablesConfigFacet: invalid id, WearablesConfig not found"); + wearablesConfigFacetWithOwner.getWearablesConfigName( + aavegotchiOwnerAddress, + aavegotchiId, + 99, + ), + ).to.be.revertedWith( + "WearablesConfigFacet: invalid id, WearablesConfig not found", + ); }); it("Should revert if invalid aavegotchi id for owner", async function () { await expect( - wearablesConfigFacetWithOwner.getWearablesConfigName(aavegotchiOwnerAddress, someoneElseAavegotchiId, 0) - ).to.be.revertedWith("WearablesConfigFacet: invalid id, WearablesConfig not found"); + wearablesConfigFacetWithOwner.getWearablesConfigName( + aavegotchiOwnerAddress, + someoneElseAavegotchiId, + 0, + ), + ).to.be.revertedWith( + "WearablesConfigFacet: invalid id, WearablesConfig not found", + ); }); - it("Should return the name of a valid wearablesConfig id", async function () { - expect( - await wearablesConfigFacetWithOwner.getWearablesConfigName(aavegotchiOwnerAddress, aavegotchiId, 0) - ).to.equal("Test"); - }); + it("Should return the name of a valid wearablesConfig id", async function () { + expect( + await wearablesConfigFacetWithOwner.getWearablesConfigName( + aavegotchiOwnerAddress, + aavegotchiId, + 0, + ), + ).to.equal("Test"); }); + }); - describe("Testing getWearablesConfigWearables", async function () { - it("Should revert if invalid wearablesConfig id", async function () { - await expect( - wearablesConfigFacetWithOwner.getWearablesConfigWearables(aavegotchiOwnerAddress, aavegotchiId, 99) - ).to.be.revertedWith("WearablesConfigFacet: invalid id, WearablesConfig not found"); - }); - it("Should revert if invalid aavegotchi id for owner", async function () { - await expect( - wearablesConfigFacetWithOwner.getWearablesConfigWearables(aavegotchiOwnerAddress, someoneElseAavegotchiId, 0) - ).to.be.revertedWith("WearablesConfigFacet: invalid id, WearablesConfig not found"); - }); - it("Should return the wearables array of a valid wearablesConfig id", async function () { - expect( - await wearablesConfigFacetWithOwner.getWearablesConfigWearables(aavegotchiOwnerAddress, aavegotchiId, 0) - ).to.eql(wearablesToStore); - }); + describe("Testing getWearablesConfigWearables", async function () { + it("Should revert if invalid wearablesConfig id", async function () { + await expect( + wearablesConfigFacetWithOwner.getWearablesConfigWearables( + aavegotchiOwnerAddress, + aavegotchiId, + 99, + ), + ).to.be.revertedWith( + "WearablesConfigFacet: invalid id, WearablesConfig not found", + ); + }); + it("Should revert if invalid aavegotchi id for owner", async function () { + await expect( + wearablesConfigFacetWithOwner.getWearablesConfigWearables( + aavegotchiOwnerAddress, + someoneElseAavegotchiId, + 0, + ), + ).to.be.revertedWith( + "WearablesConfigFacet: invalid id, WearablesConfig not found", + ); }); + it("Should return the wearables array of a valid wearablesConfig id", async function () { + expect( + await wearablesConfigFacetWithOwner.getWearablesConfigWearables( + aavegotchiOwnerAddress, + aavegotchiId, + 0, + ), + ).to.eql(wearablesToStore); + }); + }); - describe("Testing getWearablesConfig", async function () { - it("Should revert if invalid wearablesConfig id", async function () { - await expect( - wearablesConfigFacetWithOwner.getWearablesConfig(aavegotchiOwnerAddress, aavegotchiId, 99) - ).to.be.revertedWith("WearablesConfigFacet: invalid id, WearablesConfig not found"); - }); - it("Should revert if invalid aavegotchi id for owner", async function () { - await expect( - wearablesConfigFacetWithOwner.getWearablesConfig(aavegotchiOwnerAddress, someoneElseAavegotchiId, 0) - ).to.be.revertedWith("WearablesConfigFacet: invalid id, WearablesConfig not found"); + describe("Testing getWearablesConfig", async function () { + it("Should revert if invalid wearablesConfig id", async function () { + await expect( + wearablesConfigFacetWithOwner.getWearablesConfig( + aavegotchiOwnerAddress, + aavegotchiId, + 99, + ), + ).to.be.revertedWith( + "WearablesConfigFacet: invalid id, WearablesConfig not found", + ); + }); + it("Should revert if invalid aavegotchi id for owner", async function () { + await expect( + wearablesConfigFacetWithOwner.getWearablesConfig( + aavegotchiOwnerAddress, + someoneElseAavegotchiId, + 0, + ), + ).to.be.revertedWith( + "WearablesConfigFacet: invalid id, WearablesConfig not found", + ); }); it("Should return name and wearables array if valid wearablesConfig id", async function () { - const wearablesConfig = await wearablesConfigFacetWithOwner.getWearablesConfig(aavegotchiOwnerAddress, aavegotchiId, 0); + const wearablesConfig = + await wearablesConfigFacetWithOwner.getWearablesConfig( + aavegotchiOwnerAddress, + aavegotchiId, + 0, + ); expect(wearablesConfig.name).to.equal("Test"); expect(wearablesConfig.wearables).to.eql(wearablesToStore); }); @@ -212,11 +283,16 @@ describe("Testing Wearables Config", async function () { it("Should be able to update existing WearablesConfig and emit event if all parameters are valid", async function () { const newWearablesToStore = new Array(16).fill(0); const receipt = await ( - await wearablesConfigFacetWithOwner.updateWearablesConfig(aavegotchiId, 0, "New Name", newWearablesToStore) + await wearablesConfigFacetWithOwner.updateWearablesConfig( + aavegotchiId, + 0, + "New Name", + newWearablesToStore, + ) ).wait(); // check that event is emitted with the right parameters const event = receipt!.events!.find( - (event) => event.event === "WearablesConfigUpdated" + (event) => event.event === "WearablesConfigUpdated", ); const owner = event!.args!.owner; const tokenId = event!.args!.tokenId; @@ -228,40 +304,76 @@ describe("Testing Wearables Config", async function () { expect(wearables).to.eql(newWearablesToStore); // check wearablesConfig name has been updated expect( - await wearablesConfigFacetWithOwner.getWearablesConfigName(aavegotchiOwnerAddress, aavegotchiId, 0) + await wearablesConfigFacetWithOwner.getWearablesConfigName( + aavegotchiOwnerAddress, + aavegotchiId, + 0, + ), ).to.equal("New Name"); // check wearablesConfig stored wearables have been updated expect( - await wearablesConfigFacetWithOwner.getWearablesConfigWearables(aavegotchiOwnerAddress, aavegotchiId, 0) + await wearablesConfigFacetWithOwner.getWearablesConfigWearables( + aavegotchiOwnerAddress, + aavegotchiId, + 0, + ), ).to.eql(newWearablesToStore); }); it("Should not update the name if the new name is empty", async function () { - await wearablesConfigFacetWithOwner.updateWearablesConfig(aavegotchiId, 0, "", wearablesToStore) + await wearablesConfigFacetWithOwner.updateWearablesConfig( + aavegotchiId, + 0, + "", + wearablesToStore, + ); // check wearablesConfig name has been updated expect( - await wearablesConfigFacetWithOwner.getWearablesConfigName(aavegotchiOwnerAddress, aavegotchiId, 0) + await wearablesConfigFacetWithOwner.getWearablesConfigName( + aavegotchiOwnerAddress, + aavegotchiId, + 0, + ), ).to.equal("New Name"); // check wearablesConfig stored wearables have been updated expect( - await wearablesConfigFacetWithOwner.getWearablesConfigWearables(aavegotchiOwnerAddress, aavegotchiId, 0) + await wearablesConfigFacetWithOwner.getWearablesConfigWearables( + aavegotchiOwnerAddress, + aavegotchiId, + 0, + ), ).to.eql(wearablesToStore); }); it("Should revert if invalid wearablesConfig id", async function () { await expect( - wearablesConfigFacetWithOwner.updateWearablesConfig(aavegotchiId, 99, "Test", wearablesToStore) - ).to.be.revertedWith("WearablesConfigFacet: invalid id, WearablesConfig not found"); + wearablesConfigFacetWithOwner.updateWearablesConfig( + aavegotchiId, + 99, + "Test", + wearablesToStore, + ), + ).to.be.revertedWith( + "WearablesConfigFacet: invalid id, WearablesConfig not found", + ); }); }); describe("Testing wearablesConfigExists", async function () { it("Should return true for valid wearablesConfig", async function () { expect( - await wearablesConfigFacetWithOwner.wearablesConfigExists(aavegotchiOwnerAddress, aavegotchiId, 0) + await wearablesConfigFacetWithOwner.wearablesConfigExists( + aavegotchiOwnerAddress, + aavegotchiId, + 0, + ), ).to.be.true; }); it("Should return false for invalid wearablesConfig", async function () { expect( - await wearablesConfigFacetWithOwner.wearablesConfigExists(aavegotchiOwnerAddress, aavegotchiId, 99) + await wearablesConfigFacetWithOwner.wearablesConfigExists( + aavegotchiOwnerAddress, + aavegotchiId, + 99, + ), ).to.be.false; }); }); @@ -269,15 +381,28 @@ describe("Testing Wearables Config", async function () { describe("Testing getAavegotchiWearablesConfigCount", async function () { it("Should return the right amount of wearablesConfig", async function () { expect( - await wearablesConfigFacetWithOwner.getAavegotchiWearablesConfigCount(aavegotchiOwnerAddress, aavegotchiId) + await wearablesConfigFacetWithOwner.getAavegotchiWearablesConfigCount( + aavegotchiOwnerAddress, + aavegotchiId, + ), ).to.equal(1); // config #2 (id: 1) - await wearablesConfigFacetWithOwner.createWearablesConfig(aavegotchiId, "Test", wearablesToStore) + await wearablesConfigFacetWithOwner.createWearablesConfig( + aavegotchiId, + "Test", + wearablesToStore, + ); expect( - await wearablesConfigFacetWithOwner.getAavegotchiWearablesConfigCount(aavegotchiOwnerAddress, aavegotchiId) + await wearablesConfigFacetWithOwner.getAavegotchiWearablesConfigCount( + aavegotchiOwnerAddress, + aavegotchiId, + ), ).to.equal(2); expect( - await wearablesConfigFacetWithOwner.getAavegotchiWearablesConfigCount(aavegotchiOwnerAddress, 2499) + await wearablesConfigFacetWithOwner.getAavegotchiWearablesConfigCount( + aavegotchiOwnerAddress, + 2499, + ), ).to.equal(0); }); }); @@ -285,30 +410,51 @@ describe("Testing Wearables Config", async function () { describe("Testing createWearablesConfig with payment", async function () { it("Should be able to create a 3rd for free", async function () { // config #3 (id: 2) - await wearablesConfigFacetWithOwner.createWearablesConfig(aavegotchiId, "Test", wearablesToStore) + await wearablesConfigFacetWithOwner.createWearablesConfig( + aavegotchiId, + "Test", + wearablesToStore, + ); expect( - await wearablesConfigFacetWithOwner.getAavegotchiWearablesConfigCount(aavegotchiOwnerAddress, aavegotchiId) + await wearablesConfigFacetWithOwner.getAavegotchiWearablesConfigCount( + aavegotchiOwnerAddress, + aavegotchiId, + ), ).to.equal(3); }); it("Should not be able to create a 4th for free", async function () { await expect( - wearablesConfigFacetWithOwner.createWearablesConfig(aavegotchiId, "Test 4th", wearablesToStore) + wearablesConfigFacetWithOwner.createWearablesConfig( + aavegotchiId, + "Test 4th", + wearablesToStore, + ), ).to.be.revertedWith("WearablesConfigFacet: Incorrect GHST value sent"); }); it("Should not be able to create a 4th for more than 1 GHST", async function () { await expect( - wearablesConfigFacetWithOwner.createWearablesConfig(aavegotchiId, "Test 4th", wearablesToStore, { value: ethers.utils.parseEther("10") }) + wearablesConfigFacetWithOwner.createWearablesConfig( + aavegotchiId, + "Test 4th", + wearablesToStore, + { value: ethers.utils.parseEther("10") }, + ), ).to.be.revertedWith("WearablesConfigFacet: Incorrect GHST value sent"); }); it("Should be able to pay for the 4th (with event emitted)", async function () { const daoBalanceBefore = await ethers.provider.getBalance(daoAddress); - const receipt = await ( - // config #4 (id: 3) - await wearablesConfigFacetWithOwner.createWearablesConfig(aavegotchiId, "Test 4th", wearablesToStore, { value: ethers.utils.parseEther("1") }) + const receipt = await // config #4 (id: 3) + ( + await wearablesConfigFacetWithOwner.createWearablesConfig( + aavegotchiId, + "Test 4th", + wearablesToStore, + { value: ethers.utils.parseEther("1") }, + ) ).wait(); // check that event is emitted with the right parameters const event = receipt!.events!.find( - (event) => event.event === "WearablesConfigPaymentReceived" + (event) => event.event === "WearablesConfigDaoPaymentReceived", ); const owner = event!.args!.owner; const tokenId = event!.args!.tokenId; @@ -320,12 +466,21 @@ describe("Testing Wearables Config", async function () { expect(value).to.equal(ethers.utils.parseEther("1")); // compare balance before and after for dao address const daoBalanceAfter = await ethers.provider.getBalance(daoAddress); - expect(daoBalanceAfter).to.equal(daoBalanceBefore.add(ethers.utils.parseEther("1"))); + expect(daoBalanceAfter).to.equal( + daoBalanceBefore.add(ethers.utils.parseEther("1")), + ); expect( - await wearablesConfigFacetWithOwner.getWearablesConfigName(aavegotchiOwnerAddress, aavegotchiId, 3) + await wearablesConfigFacetWithOwner.getWearablesConfigName( + aavegotchiOwnerAddress, + aavegotchiId, + 3, + ), ).to.equal("Test 4th"); }); }); + // + // Note: Disabled until the realm diamond is set on Geist as it prevents to accept a lending + // //describe("Testing for rented gotchis", async function () { // // it("Should be able to save for a rented gotchi", async function () { @@ -362,7 +517,7 @@ describe("Testing Wearables Config", async function () { // ); // // const lending = await lendingGetterFacet.getGotchiLendingFromToken( - // aavegotchiId + // aavegotchiId // ); // expect(lending.borrower).to.equal(anotherAavegotchiOwnerAddress); // @@ -385,60 +540,240 @@ describe("Testing Wearables Config", async function () { // it("Should revert on create for rented out gotchi", async function () { // await expect( // wearablesConfigFacetWithOwner.createWearablesConfig(aavegotchiId, "Test Rented Out", wearablesToStore, { value: ethers.utils.parseEther("1") }) - // ).to.be.revertedWith("LibAppStorage: Only aavegotchi owner can call this function"); + // ).to.be.revertedWith("LibAppStorage: Only aavegotchi owner can call this function"); // }); //}); + describe("Testing for unbridged gotchis", async function () { + it("Should be able to create for unbridged gotchi for free", async function () { + // unbridged config #1 (id: 0) + await wearablesConfigFacetWithOwner.createWearablesConfig( + 24999, + "Test Create for Aavegotchi not Bridged", + wearablesToStore, + ); + expect( + await wearablesConfigFacetWithOwner.getWearablesConfigName( + aavegotchiOwnerAddress, + 24999, + 0, + ), + ).to.equal("Test Create for Aavegotchi not Bridged"); + }); + it("Should have to pay for the 4th slot even for unbridged gotchis", async function () { + const daoBalanceBefore = await ethers.provider.getBalance(daoAddress); + // unbridged config #2 (id: 1) + await wearablesConfigFacetWithOwner.createWearablesConfig( + 24999, + "Test Create for Aavegotchi not Bridged", + wearablesToStore, + ); + // unbridged config #3 (id: 2) + await wearablesConfigFacetWithOwner.createWearablesConfig( + 24999, + "Test Create for Aavegotchi not Bridged", + wearablesToStore, + ); + // unbridged config #4 (id: 3) + await wearablesConfigFacetWithOwner.createWearablesConfig( + 24999, + "Test Create 4th for Aavegotchi not Bridged", + wearablesToStore, + { value: ethers.utils.parseEther("1") }, + ); + expect( + await wearablesConfigFacetWithOwner.getWearablesConfigName( + aavegotchiOwnerAddress, + 24999, + 3, + ), + ).to.equal("Test Create 4th for Aavegotchi not Bridged"); + const daoBalanceAfter = await ethers.provider.getBalance(daoAddress); + expect(daoBalanceAfter).to.equal( + daoBalanceBefore.add(ethers.utils.parseEther("1")), + ); + }); + it("Should be able to update for unbridged gotchi for free", async function () { + await wearablesConfigFacetWithOwner.updateWearablesConfig( + 24999, + 0, + "Test Update for Aavegotchi not Bridged", + wearablesToStore, + ); + expect( + await wearablesConfigFacetWithOwner.getWearablesConfigName( + aavegotchiOwnerAddress, + 24999, + 0, + ), + ).to.equal("Test Update for Aavegotchi not Bridged"); + }); + }); + describe("Testing for other owners gotchis", async function () { + it("Should have to pay a fee for a gotchi not owned (create)", async function () { + const owner = await aavegotchiFacet.ownerOf(someoneElseAavegotchiId); + const ownerBalanceBefore = await ethers.provider.getBalance(owner); + // alt config #1 (id: 0) + await wearablesConfigFacetWithOwner.createWearablesConfig( + someoneElseAavegotchiId, + "Test Create for Aavegotchi not Owned", + wearablesToStore, + { value: ethers.utils.parseEther("0.1") }, + ); + const ownerBalanceAfter = await ethers.provider.getBalance(owner); + expect(ownerBalanceAfter).to.equal( + ownerBalanceBefore.add(ethers.utils.parseEther("0.1")), + ); + expect( + await wearablesConfigFacetWithOwner.getWearablesConfigName( + owner, + someoneElseAavegotchiId, + 0, + ), + ).to.equal("Test Create for Aavegotchi not Owned"); + }); + it("Should have to pay a fee in addition of the slot for a gotchi not owned (create)", async function () { + const owner = await aavegotchiFacet.ownerOf(someoneElseAavegotchiId); + const ownerBalanceBefore = await ethers.provider.getBalance(owner); + const daoBalanceBefore = await ethers.provider.getBalance(daoAddress); + // alt config #2 (id: 1) + await wearablesConfigFacetWithOwner.createWearablesConfig( + someoneElseAavegotchiId, + "Test Create for Aavegotchi not Owned", + wearablesToStore, + { value: ethers.utils.parseEther("0.1") }, + ); + // alt config #3 (id: 2) + await wearablesConfigFacetWithOwner.createWearablesConfig( + someoneElseAavegotchiId, + "Test Create for Aavegotchi not Owned", + wearablesToStore, + { value: ethers.utils.parseEther("0.1") }, + ); + // alt config #4 (id: 3) + await wearablesConfigFacetWithOwner.createWearablesConfig( + someoneElseAavegotchiId, + "Test Create for Aavegotchi not Owned", + wearablesToStore, + { value: ethers.utils.parseEther("1.1") }, + ); + const ownerBalanceAfter = await ethers.provider.getBalance(owner); + expect(ownerBalanceAfter).to.equal( + ownerBalanceBefore.add(ethers.utils.parseEther("0.3")), + ); + const daoBalanceAfter = await ethers.provider.getBalance(daoAddress); + expect(daoBalanceAfter).to.equal( + daoBalanceBefore.add(ethers.utils.parseEther("1")), + ); + }); + it("Should require a fee for a gotchi not owned (update)", async function () { + const owner = await aavegotchiFacet.ownerOf(someoneElseAavegotchiId); + const ownerBalanceBefore = await ethers.provider.getBalance(owner); + await wearablesConfigFacetWithOwner.updateWearablesConfig( + someoneElseAavegotchiId, + 0, + "", + wearablesToStore, + { value: ethers.utils.parseEther("0.1") }, + ); + const ownerBalanceAfter = await ethers.provider.getBalance(owner); + expect(ownerBalanceAfter).to.equal( + ownerBalanceBefore.add(ethers.utils.parseEther("0.1")), + ); + }); + }); describe("Testing for invalid gotchis", async function () { it("Should revert for a portal", async function () { await expect( - wearablesConfigFacetWithOwner.createWearablesConfig(unsummonedAavegotchiId, "Test Portal", wearablesToStore, { value: ethers.utils.parseEther("1") }) - ).to.be.revertedWith("WearablesConfigFacet: Can only create wearables config for Aavegotchi"); - }); - it("Should revert for a gotchi not owned (create)", async function () { - await expect( - wearablesConfigFacetWithOwner.createWearablesConfig(someoneElseAavegotchiId, "Test CreateAavegotchi not owned", wearablesToStore, { value: ethers.utils.parseEther("1") }) - ).to.be.revertedWith("LibAppStorage: Only aavegotchi owner can call this function"); + wearablesConfigFacetWithOwner.createWearablesConfig( + unsummonedAavegotchiId, + "Test Portal", + wearablesToStore, + ), + ).to.be.revertedWith( + "WearablesConfigFacet: Not allowed to create wearables config", + ); }); - it("Should revert for a gotchi not owned (update)", async function () { + it("Should revert for an invalid id (over max supply)", async function () { await expect( - wearablesConfigFacetWithOwner.updateWearablesConfig(someoneElseAavegotchiId, 0, "", wearablesToStore) - ).to.be.revertedWith("LibAppStorage: Only aavegotchi owner can call this function"); + wearablesConfigFacetWithOwner.createWearablesConfig( + 25000, + "Test Invalid Id", + wearablesToStore, + ), + ).to.be.revertedWith( + "LibWearablesConfig: Invalid tokenId for unbridged aavegotchi", + ); }); }); describe("Testing for invalid wearables", async function () { it("Should revert for an invalid wearable id (create)", async function () { await expect( - wearablesConfigFacetWithOwner.createWearablesConfig(aavegotchiId, "Test Invalid Wearable Id", wearablesToStoreWithInvalidId, { value: ethers.utils.parseEther("1") }) + wearablesConfigFacetWithOwner.createWearablesConfig( + aavegotchiId, + "Test Invalid Wearable Id", + wearablesToStoreWithInvalidId, + { value: ethers.utils.parseEther("1") }, + ), ).to.be.revertedWith("LibWearablesConfig: Item type does not exist"); }); it("Should revert for an invalid wearable id (update)", async function () { await expect( - wearablesConfigFacetWithOwner.updateWearablesConfig(aavegotchiId, 0, "", wearablesToStoreWithInvalidId) + wearablesConfigFacetWithOwner.updateWearablesConfig( + aavegotchiId, + 0, + "", + wearablesToStoreWithInvalidId, + ), ).to.be.revertedWith("LibWearablesConfig: Item type does not exist"); }); it("Should revert for an invalid wearable slot (create)", async function () { await expect( - wearablesConfigFacetWithOwner.createWearablesConfig(aavegotchiId, "Test Invalid Wearable Slot", wearablesToStoreWithInvalidSlot, { value: ethers.utils.parseEther("1") }) - ).to.be.revertedWith("WearablesConfigFacet: Invalid wearables"); + wearablesConfigFacetWithOwner.createWearablesConfig( + aavegotchiId, + "Test Invalid Wearable Slot", + wearablesToStoreWithInvalidSlot, + { value: ethers.utils.parseEther("1") }, + ), + ).to.be.revertedWith("WearablesConfigFacet: Invalid wearables"); }); it("Should revert for an invalid wearable slot (update)", async function () { await expect( - wearablesConfigFacetWithOwner.updateWearablesConfig(aavegotchiId, 0, "", wearablesToStoreWithInvalidSlot) - ).to.be.revertedWith("WearablesConfigFacet: Invalid wearables"); + wearablesConfigFacetWithOwner.updateWearablesConfig( + aavegotchiId, + 0, + "", + wearablesToStoreWithInvalidSlot, + ), + ).to.be.revertedWith("WearablesConfigFacet: Invalid wearables"); + }); + }); + describe("Testing for max wearable configs", async function () { + it("Shouldn't be able to create more than 255", async function () { + for (let i = 4; i < 255; i++) { + // config #5-255 (id: 4-254) + await wearablesConfigFacetWithOwner.createWearablesConfig( + aavegotchiId, + "Test", + wearablesToStore, + { value: ethers.utils.parseEther("1") }, + ); + } + expect( + await wearablesConfigFacetWithOwner.getAavegotchiWearablesConfigCount( + aavegotchiOwnerAddress, + aavegotchiId, + ), + ).to.equal(255); + await expect( + wearablesConfigFacetWithOwner.createWearablesConfig( + aavegotchiId, + "Test 256th", + wearablesToStore, + { value: ethers.utils.parseEther("1") }, + ), + ).to.be.revertedWith( + "WearablesConfigFacet: No more wearables config slots available", + ); }); }); - //describe("Testing for max wearable configs", async function () { - // it("Shouldn't be able to create more than 255", async function () { - // for (let i = 4; i < 255; i++) { - // // config #5-255 (id: 4-254) - // await wearablesConfigFacetWithOwner.createWearablesConfig(aavegotchiId, "Test", wearablesToStore, { value: ethers.utils.parseEther("1") }); - // } - // expect( - // await wearablesConfigFacetWithOwner.getAavegotchiWearablesConfigCount(aavegotchiOwnerAddress, aavegotchiId) - // ).to.equal(255); - // await expect( - // wearablesConfigFacetWithOwner.createWearablesConfig(aavegotchiId, "Test 256th", wearablesToStore, { value: ethers.utils.parseEther("1") }) - // ).to.be.revertedWith("WearablesConfigFacet: No more wearables config slots available"); - // }); - //}); }); From 31dc0cfb77f7cc99820041218d3195620fbc8fbb Mon Sep 17 00:00:00 2001 From: pgendreau Date: Thu, 23 Jan 2025 19:56:09 -0500 Subject: [PATCH 7/8] AGIP135: raise maximum number of slots to 65535 --- .../facets/WearablesConfigFacet.sol | 30 +++++++++---------- .../Aavegotchi/libraries/LibAppStorage.sol | 2 +- .../libraries/LibWearablesConfig.sol | 4 +-- .../upgrades/upgrade-wearablesConfigFacet.ts | 10 +++---- test/wearablesConfigTest.ts | 29 ------------------ 5 files changed, 23 insertions(+), 52 deletions(-) diff --git a/contracts/Aavegotchi/facets/WearablesConfigFacet.sol b/contracts/Aavegotchi/facets/WearablesConfigFacet.sol index 312c854e..a5665b2a 100644 --- a/contracts/Aavegotchi/facets/WearablesConfigFacet.sol +++ b/contracts/Aavegotchi/facets/WearablesConfigFacet.sol @@ -9,18 +9,18 @@ import {LibWearablesConfig} from "../libraries/LibWearablesConfig.sol"; contract WearablesConfigFacet is Modifiers { // constants - uint256 private constant MAX_UINT8 = 2**8 - 1; - uint8 public constant WEARABLESCONFIG_FREE_SLOTS = 3; + uint16 public constant WEARABLESCONFIG_MAX_SLOTS = 2**16 - 1; + uint16 public constant WEARABLESCONFIG_FREE_SLOTS = 3; uint256 public constant WEARABLESCONFIG_SLOT_PRICE = 1000000000000000000; // 1 GHST uint256 public constant WEARABLESCONFIG_OWNER_FEE = 100000000000000000; // 0.1 GHST // events - event WearablesConfigCreated(address indexed owner, uint256 indexed tokenId, uint8 indexed wearablesConfigId, uint16[EQUIPPED_WEARABLE_SLOTS] wearables, uint256 value); - event WearablesConfigUpdated(address indexed owner, uint256 indexed tokenId, uint8 indexed wearablesConfigId, uint16[EQUIPPED_WEARABLE_SLOTS] wearables); - event WearablesConfigDaoPaymentReceived(address indexed owner, uint256 indexed tokenId, uint8 indexed wearablesConfigId, uint256 value); - event WearablesConfigOwnerPaymentReceived(address indexed sender, address indexed owner, uint256 indexed tokenId, uint8 wearablesConfigId, uint256 value); + event WearablesConfigCreated(address indexed owner, uint256 indexed tokenId, uint16 indexed wearablesConfigId, uint16[EQUIPPED_WEARABLE_SLOTS] wearables, uint256 value); + event WearablesConfigUpdated(address indexed owner, uint256 indexed tokenId, uint16 indexed wearablesConfigId, uint16[EQUIPPED_WEARABLE_SLOTS] wearables); + event WearablesConfigDaoPaymentReceived(address indexed owner, uint256 indexed tokenId, uint16 indexed wearablesConfigId, uint256 value); + event WearablesConfigOwnerPaymentReceived(address indexed sender, address indexed owner, uint256 indexed tokenId, uint16 wearablesConfigId, uint256 value); - /// @notice Creates and stores a new wearables configuration (max 255 per Aavegotchi per owner). + /// @notice Creates and stores a new wearables configuration (max 65535 per Aavegotchi per owner). /// @notice First three slots are free, the rest are paid. /// @notice To create a wearables config for someone else aavegotchi there is a fee /// @notice We support wearables config creation for unbridged gotchis (config owner is set to sender) @@ -35,7 +35,7 @@ contract WearablesConfigFacet is Modifiers { ) external payable - returns (uint8 wearablesConfigId) + returns (uint16 wearablesConfigId) { // check that creation of this wearables config is allowed (only aavegotchi or unbridged) require(LibWearablesConfig._checkAavegotchiOrUnbridged(_tokenId), "WearablesConfigFacet: Not allowed to create wearables config"); @@ -58,7 +58,7 @@ contract WearablesConfigFacet is Modifiers { // get the next available slot wearablesConfigId = LibWearablesConfig._getNextWearablesConfigId(owner, _tokenId); // solidity will throw if slots used overflows - require(wearablesConfigId < 2**8 - 1, "WearablesConfigFacet: No more wearables config slots available"); + require(wearablesConfigId < WEARABLESCONFIG_MAX_SLOTS, "WearablesConfigFacet: No more wearables config slots available"); // if the owner has reached the free slots limit then they need to pay for the extra slot if (wearablesConfigId >= WEARABLESCONFIG_FREE_SLOTS) { @@ -109,7 +109,7 @@ contract WearablesConfigFacet is Modifiers { /// @dev if _name is empty, only wearables are updated. function updateWearablesConfig( uint256 _tokenId, - uint8 _wearablesConfigId, + uint16 _wearablesConfigId, string calldata _name, uint16[EQUIPPED_WEARABLE_SLOTS] calldata _wearablesToStore ) @@ -156,7 +156,7 @@ contract WearablesConfigFacet is Modifiers { /// @param _tokenId The ID of the aavegotchi to update the wearables configuration for /// @param _wearablesConfigId The ID of the wearables configuration to update /// @return exists true if the wearables config exists - function wearablesConfigExists(address _owner, uint256 _tokenId, uint8 _wearablesConfigId) external view returns (bool exists) { + function wearablesConfigExists(address _owner, uint256 _tokenId, uint16 _wearablesConfigId) external view returns (bool exists) { exists = LibWearablesConfig._wearablesConfigExists(_owner, _tokenId, _wearablesConfigId); } @@ -165,7 +165,7 @@ contract WearablesConfigFacet is Modifiers { /// @param _tokenId The ID of the aavegotchi to update the wearables configuration for /// @param _wearablesConfigId The ID of the wearables configuration to update /// @return wearablesConfig The wearables config - function getWearablesConfig(address _owner, uint256 _tokenId, uint8 _wearablesConfigId) external view returns (WearablesConfig memory wearablesConfig) { + function getWearablesConfig(address _owner, uint256 _tokenId, uint16 _wearablesConfigId) external view returns (WearablesConfig memory wearablesConfig) { require(LibWearablesConfig._wearablesConfigExists(_owner, _tokenId, _wearablesConfigId), "WearablesConfigFacet: invalid id, WearablesConfig not found"); return s.gotchiWearableConfigs[_tokenId][_owner][_wearablesConfigId]; } @@ -175,7 +175,7 @@ contract WearablesConfigFacet is Modifiers { /// @param _tokenId The ID of the aavegotchi to update the wearables configuration for /// @param _wearablesConfigId The ID of the wearables configuration to update /// @return name The name of the wearables config - function getWearablesConfigName(address _owner, uint256 _tokenId, uint8 _wearablesConfigId) external view returns (string memory name) { + function getWearablesConfigName(address _owner, uint256 _tokenId, uint16 _wearablesConfigId) external view returns (string memory name) { require(LibWearablesConfig._wearablesConfigExists(_owner, _tokenId, _wearablesConfigId), "WearablesConfigFacet: invalid id, WearablesConfig not found"); name = s.gotchiWearableConfigs[_tokenId][_owner][_wearablesConfigId].name; } @@ -185,7 +185,7 @@ contract WearablesConfigFacet is Modifiers { /// @param _tokenId The ID of the aavegotchi to update the wearables configuration for /// @param _wearablesConfigId The ID of the wearables configuration to update /// @return wearables The wearables stored for this wearables config - function getWearablesConfigWearables(address _owner, uint256 _tokenId, uint8 _wearablesConfigId) external view returns (uint16[EQUIPPED_WEARABLE_SLOTS] memory wearables) { + function getWearablesConfigWearables(address _owner, uint256 _tokenId, uint16 _wearablesConfigId) external view returns (uint16[EQUIPPED_WEARABLE_SLOTS] memory wearables) { require(LibWearablesConfig._wearablesConfigExists(_owner, _tokenId, _wearablesConfigId), "WearablesConfigFacet: invalid id, WearablesConfig not found"); wearables = s.gotchiWearableConfigs[_tokenId][_owner][_wearablesConfigId].wearables; } @@ -194,7 +194,7 @@ contract WearablesConfigFacet is Modifiers { /// @param _owner The owner of the aavegotchi /// @param _tokenId The ID of the aavegotchi to update the wearables configuration for /// @return slotsUsed The number of wearables configs - function getAavegotchiWearablesConfigCount(address _owner, uint256 _tokenId) external view returns (uint8 slotsUsed) { + function getAavegotchiWearablesConfigCount(address _owner, uint256 _tokenId) external view returns (uint16 slotsUsed) { slotsUsed = s.ownerGotchiSlotsUsed[_owner][_tokenId]; } } diff --git a/contracts/Aavegotchi/libraries/LibAppStorage.sol b/contracts/Aavegotchi/libraries/LibAppStorage.sol index 833cd360..a5f3e304 100644 --- a/contracts/Aavegotchi/libraries/LibAppStorage.sol +++ b/contracts/Aavegotchi/libraries/LibAppStorage.sol @@ -395,7 +395,7 @@ struct AppStorage { // gotchi => owner => wearable configs mapping(uint256 => mapping(address => WearablesConfig[])) gotchiWearableConfigs; // owner => gotchi => slots used - mapping(address => mapping (uint256 => uint8)) ownerGotchiSlotsUsed; + mapping(address => mapping (uint256 => uint16)) ownerGotchiSlotsUsed; } library LibAppStorage { diff --git a/contracts/Aavegotchi/libraries/LibWearablesConfig.sol b/contracts/Aavegotchi/libraries/LibWearablesConfig.sol index fc39f47d..274828c6 100644 --- a/contracts/Aavegotchi/libraries/LibWearablesConfig.sol +++ b/contracts/Aavegotchi/libraries/LibWearablesConfig.sol @@ -32,7 +32,7 @@ library LibWearablesConfig { /// @param _owner The owner of the gotchi /// @param _tokenId The tokenId of the gotchi /// @return nextWearablesConfigId The next free wearables config id - function _getNextWearablesConfigId(address _owner, uint256 _tokenId) internal view returns (uint8 nextWearablesConfigId) { + function _getNextWearablesConfigId(address _owner, uint256 _tokenId) internal view returns (uint16 nextWearablesConfigId) { AppStorage storage s = LibAppStorage.diamondStorage(); // slots start at 0 so slotsUsed is always the next config id nextWearablesConfigId = s.ownerGotchiSlotsUsed[_owner][_tokenId]; @@ -43,7 +43,7 @@ library LibWearablesConfig { /// @param _tokenId The tokenId of the gotchi /// @param _wearablesConfigId The wearables config id /// @return exists True if the wearables config exists false otherwise - function _wearablesConfigExists(address _owner, uint256 _tokenId, uint8 _wearablesConfigId) internal view returns (bool exists) { + function _wearablesConfigExists(address _owner, uint256 _tokenId, uint16 _wearablesConfigId) internal view returns (bool exists) { AppStorage storage s = LibAppStorage.diamondStorage(); // slots start at 0 so slots used should always be greater by 1 than the last config id exists = (s.ownerGotchiSlotsUsed[_owner][_tokenId] > _wearablesConfigId); diff --git a/scripts/upgrades/upgrade-wearablesConfigFacet.ts b/scripts/upgrades/upgrade-wearablesConfigFacet.ts index 9cb41bb7..6bc3168f 100644 --- a/scripts/upgrades/upgrade-wearablesConfigFacet.ts +++ b/scripts/upgrades/upgrade-wearablesConfigFacet.ts @@ -12,12 +12,12 @@ export async function upgrade() { facetName: "WearablesConfigFacet", addSelectors: [ 'function createWearablesConfig(uint256 _tokenId, string _name, uint16[16] _wearablesToStore) external payable', - 'function updateWearablesConfig(uint256 _tokenId, uint8 _wearablesConfigId, string _name, uint16[16] _wearablesToStore) external', - 'function getWearablesConfig(address _owner, uint256 _tokenId, uint8 _wearablesConfigId) external view', - 'function getWearablesConfigName(address _owner, uint256 _tokenId, uint8 _wearablesConfigId) external view', - 'function getWearablesConfigWearables(address _owner, uint256 _tokenId, uint8 _wearablesConfigId) external view', + 'function updateWearablesConfig(uint256 _tokenId, uint16 _wearablesConfigId, string _name, uint16[16] _wearablesToStore) external', + 'function getWearablesConfig(address _owner, uint256 _tokenId, uint16 _wearablesConfigId) external view', + 'function getWearablesConfigName(address _owner, uint256 _tokenId, uint16 _wearablesConfigId) external view', + 'function getWearablesConfigWearables(address _owner, uint256 _tokenId, uint16 _wearablesConfigId) external view', 'function getAavegotchiWearablesConfigCount(address _owner, uint256 _tokenId) external view', - 'function wearablesConfigExists(address _owner, uint256 _tokenId, uint8 _wearablesConfigId) external view', + 'function wearablesConfigExists(address _owner, uint256 _tokenId, uint16 _wearablesConfigId) external view', ], removeSelectors: [], }, diff --git a/test/wearablesConfigTest.ts b/test/wearablesConfigTest.ts index 70b75c45..f788dadb 100644 --- a/test/wearablesConfigTest.ts +++ b/test/wearablesConfigTest.ts @@ -747,33 +747,4 @@ describe("Testing Wearables Config", async function () { ).to.be.revertedWith("WearablesConfigFacet: Invalid wearables"); }); }); - describe("Testing for max wearable configs", async function () { - it("Shouldn't be able to create more than 255", async function () { - for (let i = 4; i < 255; i++) { - // config #5-255 (id: 4-254) - await wearablesConfigFacetWithOwner.createWearablesConfig( - aavegotchiId, - "Test", - wearablesToStore, - { value: ethers.utils.parseEther("1") }, - ); - } - expect( - await wearablesConfigFacetWithOwner.getAavegotchiWearablesConfigCount( - aavegotchiOwnerAddress, - aavegotchiId, - ), - ).to.equal(255); - await expect( - wearablesConfigFacetWithOwner.createWearablesConfig( - aavegotchiId, - "Test 256th", - wearablesToStore, - { value: ethers.utils.parseEther("1") }, - ), - ).to.be.revertedWith( - "WearablesConfigFacet: No more wearables config slots available", - ); - }); - }); }); From 666a06be5d83449a35b35aa420cd5326d4494f5c Mon Sep 17 00:00:00 2001 From: pgendreau Date: Thu, 23 Jan 2025 20:40:39 -0500 Subject: [PATCH 8/8] AGIP135: prevent updates to other owners wearables config --- .../facets/WearablesConfigFacet.sol | 20 ++++++---------- test/wearablesConfigTest.ts | 23 ++++++++----------- 2 files changed, 17 insertions(+), 26 deletions(-) diff --git a/contracts/Aavegotchi/facets/WearablesConfigFacet.sol b/contracts/Aavegotchi/facets/WearablesConfigFacet.sol index a5665b2a..2c9c4fd6 100644 --- a/contracts/Aavegotchi/facets/WearablesConfigFacet.sol +++ b/contracts/Aavegotchi/facets/WearablesConfigFacet.sol @@ -115,31 +115,25 @@ contract WearablesConfigFacet is Modifiers { ) external payable { - address sender = LibMeta.msgSender(); - address owner = s.aavegotchis[_tokenId].owner; - // check that update of this wearables config is allowed (only aavegotchi or unbridged) require(LibWearablesConfig._checkAavegotchiOrUnbridged(_tokenId), "WearablesConfigFacet: Not allowed to update wearables config"); // check that wearables are valid and for the right slots require(LibWearablesConfig._checkValidWearables(_wearablesToStore), "WearablesConfigFacet: Invalid wearables"); + address sender = LibMeta.msgSender(); + address owner = s.aavegotchis[_tokenId].owner; + if (owner == address(0)) { - // set the owner to the sender for unbridged gotchis + // save the wearables config under the sender for unbridged aavegotchis owner = sender; + } else { + // make sure that the sender is also the owner of this aavegotchi + require(sender == owner, "WearablesConfigFacet: Only the owner can update wearables config"); } // make sure we are updating an existing wearables config require(LibWearablesConfig._wearablesConfigExists(owner, _tokenId, _wearablesConfigId), "WearablesConfigFacet: invalid id, WearablesConfig not found"); - // if the sender is not the owner and the gotchi has been bridged - // then they need to pay a fee to the owner - if (sender != owner) { - (bool success, ) = payable(owner).call{value: WEARABLESCONFIG_OWNER_FEE}(""); - require(success, "WearablesConfigFacet: Failed to send GHST to owner"); - - emit WearablesConfigOwnerPaymentReceived(sender, owner, _tokenId, _wearablesConfigId, WEARABLESCONFIG_OWNER_FEE); - } - // skip if name is empty if (bytes(_name).length > 0) { s.gotchiWearableConfigs[_tokenId][owner][_wearablesConfigId].name = _name; diff --git a/test/wearablesConfigTest.ts b/test/wearablesConfigTest.ts index f788dadb..c22708c5 100644 --- a/test/wearablesConfigTest.ts +++ b/test/wearablesConfigTest.ts @@ -665,19 +665,16 @@ describe("Testing Wearables Config", async function () { daoBalanceBefore.add(ethers.utils.parseEther("1")), ); }); - it("Should require a fee for a gotchi not owned (update)", async function () { - const owner = await aavegotchiFacet.ownerOf(someoneElseAavegotchiId); - const ownerBalanceBefore = await ethers.provider.getBalance(owner); - await wearablesConfigFacetWithOwner.updateWearablesConfig( - someoneElseAavegotchiId, - 0, - "", - wearablesToStore, - { value: ethers.utils.parseEther("0.1") }, - ); - const ownerBalanceAfter = await ethers.provider.getBalance(owner); - expect(ownerBalanceAfter).to.equal( - ownerBalanceBefore.add(ethers.utils.parseEther("0.1")), + it("Should revert for a gotchi not owned (update)", async function () { + await expect( + wearablesConfigFacetWithOwner.updateWearablesConfig( + someoneElseAavegotchiId, + 0, + "", + wearablesToStore, + ), + ).to.be.revertedWith( + "WearablesConfigFacet: Only the owner can update wearables config", ); }); });