From 92768d7a630e969830de0e372334396c8c0adbda Mon Sep 17 00:00:00 2001 From: Sing <16409791+singyiu@users.noreply.github.com> Date: Fri, 20 Oct 2023 23:30:11 -0700 Subject: [PATCH 1/9] feat: consumeResourcesToBuildFacilityType --- .../contracts/src/systems/FacilitySystem.sol | 27 ++++++++++++++++--- .../contracts/test/FacilitySystemTest.t.sol | 17 +++++++++++- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/packages/contracts/src/systems/FacilitySystem.sol b/packages/contracts/src/systems/FacilitySystem.sol index 1d53499..6289fcf 100644 --- a/packages/contracts/src/systems/FacilitySystem.sol +++ b/packages/contracts/src/systems/FacilitySystem.sol @@ -7,8 +7,10 @@ import { getKeysWithValue } from "@latticexyz/world-modules/src/modules/keyswith import { getKeysInTable } from "@latticexyz/world-modules/src/modules/keysintable/getKeysInTable.sol"; import { PackedCounter } from "@latticexyz/store/src/PackedCounter.sol"; -import { Counter, Position, PositionTableId, Orientation, EntityType, OwnedBy, EntityCustomization } from "../codegen/index.sol"; +import { Counter, Position, PositionTableId, Orientation, EntityType, OwnedBy, EntityCustomization, GameSetting } from "../codegen/index.sol"; import { positionToEntityKey } from "../positionToEntityKey.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; contract FacilitySystem is System { /** @@ -44,7 +46,16 @@ contract FacilitySystem is System { */ function canPlayerBuildFacilityType(address player, uint32 entityTypeId) public view returns (bool) { //TODO: require player to have enough resources to build this facility - return true; + + //For now, simply require player to have 10 LAPU to build any facility + IERC20 lapu = IERC20(GameSetting.getLapuVaultAddress()); + return lapu.balanceOf(player) >= 10; + } + + function consumeResourcesToBuildFacilityType(address consumer, uint32 entityTypeId) public { + //transfer 10 LAPU from consumer to this world contract + IERC20 lapu = IERC20(GameSetting.getLapuVaultAddress()); + SafeERC20.safeTransferFrom(lapu, consumer, address(this), 10); } /** @@ -88,12 +99,20 @@ contract FacilitySystem is System { * @param yaw The yaw of the facility to build. * @return The key of the entity that was created. */ - function buildFacility(uint32 entityTypeId, int32 x, int32 y, int32 z, int32 yaw, string calldata color, uint32 variant) public returns (bytes32) { + function buildFacility( + uint32 entityTypeId, + int32 x, + int32 y, + int32 z, + int32 yaw, + string calldata color, + uint32 variant + ) public returns (bytes32) { require(_msgSender() != address(0), "Invalid sender address"); require(canPlayerBuildFacilityType(_msgSender(), entityTypeId), "Cannot build this facility type"); require(canBuildFacilityTypeAtPosition(entityTypeId, x, y, z), "This facility cannot be built at this position"); - //TODO: consume resources from sender to build facility + consumeResourcesToBuildFacilityType(_msgSender(), entityTypeId); //create entity and assign component values bytes32 entityKey = positionToEntityKey(x, y, z); diff --git a/packages/contracts/test/FacilitySystemTest.t.sol b/packages/contracts/test/FacilitySystemTest.t.sol index cfc8b63..d3d51cd 100644 --- a/packages/contracts/test/FacilitySystemTest.t.sol +++ b/packages/contracts/test/FacilitySystemTest.t.sol @@ -6,17 +6,32 @@ import { MudTest } from "@latticexyz/world/test/MudTest.t.sol"; import { getKeysWithValue } from "@latticexyz/world-modules/src/modules/keyswithvalue/getKeysWithValue.sol"; import { IWorld } from "../src/codegen/world/IWorld.sol"; -import { Counter, CounterTableId, Position, PositionData } from "../src/codegen/index.sol"; +import { Counter, CounterTableId, Position, PositionData, GameSetting } from "../src/codegen/index.sol"; import { positionToEntityKey } from "../src/positionToEntityKey.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "../src/interfaces/IMockERC20.sol"; +import "../src/interfaces/ILapuVault.sol"; contract FacilitySystemTest is MudTest { IWorld public world; uint32 public constant entityTypeIdGroundLevel = 10; uint32 public constant entityTypeIdNonGroundLevel = 999; + //address player01 = address(0x5E11E1); // random address + function setUp() public override { super.setUp(); world = IWorld(worldAddress); + + //fund this contract with 1000 LAPU and approve it to be spent + //vm.startPrank(player01); + IMockERC20 dai = IMockERC20(GameSetting.getDaiAddress()); + dai.faucet(address(this), 1000); + ILapuVault lapuVault = ILapuVault(GameSetting.getLapuVaultAddress()); + IERC20(address(dai)).approve(address(lapuVault), 1000); + lapuVault.deposit(1000, address(this)); + IERC20(address(lapuVault)).approve(address(world), 1000); + //vm.stopPrank(); } function testWorldExists() public { From bf1c554dbd21847257aa88ad8a33d995d32a13fa Mon Sep 17 00:00:00 2001 From: Sing <16409791+singyiu@users.noreply.github.com> Date: Sat, 21 Oct 2023 07:54:41 -0700 Subject: [PATCH 2/9] feat: lapuCostToBuildFacilityType --- packages/contracts/mud.config.ts | 21 ++++++++++++++- packages/contracts/script/PostDeploy.s.sol | 2 ++ .../contracts/src/systems/FacilitySystem.sol | 26 ++++++++++++++----- 3 files changed, 41 insertions(+), 8 deletions(-) diff --git a/packages/contracts/mud.config.ts b/packages/contracts/mud.config.ts index ceb5202..3aa11a2 100644 --- a/packages/contracts/mud.config.ts +++ b/packages/contracts/mud.config.ts @@ -13,6 +13,7 @@ export default mudConfig({ defiPoolAddress: "address", daiAddress: "address", lapuVaultAddress: "address", + totalResidence: "uint256", totalRewarded: "uint256", }, }, @@ -39,6 +40,24 @@ export default mudConfig({ typeId: "uint32", }, }, + EntityTypeDetail: { + keySchema: { + entityTypeId: "uint32", + }, + valueSchema: { + buildingCostLapu: "uint256", + isResidence: "bool", + }, + }, + PlayerDataDetail: { + keySchema: { + playerId: "address", + }, + valueSchema: { + residence: "uint256", + rewarded: "uint256", + }, + }, OwnedBy: { valueSchema: { owner: "address", @@ -49,7 +68,7 @@ export default mudConfig({ { name: "KeysInTableModule", root: true, - args: [resolveTableId("Position")], + args: [resolveTableId("Position"), resolveTableId("PlayerDataDetail")], }, { name: "KeysWithValueModule", diff --git a/packages/contracts/script/PostDeploy.s.sol b/packages/contracts/script/PostDeploy.s.sol index 450cc65..f28612d 100644 --- a/packages/contracts/script/PostDeploy.s.sol +++ b/packages/contracts/script/PostDeploy.s.sol @@ -55,6 +55,8 @@ contract PostDeploy is Script { address(lapuVault) ); + IWorld(worldAddress).facilitySystemSetupEntityTypeDetails(); + vm.stopBroadcast(); } } diff --git a/packages/contracts/src/systems/FacilitySystem.sol b/packages/contracts/src/systems/FacilitySystem.sol index 6289fcf..98c4fd7 100644 --- a/packages/contracts/src/systems/FacilitySystem.sol +++ b/packages/contracts/src/systems/FacilitySystem.sol @@ -7,7 +7,7 @@ import { getKeysWithValue } from "@latticexyz/world-modules/src/modules/keyswith import { getKeysInTable } from "@latticexyz/world-modules/src/modules/keysintable/getKeysInTable.sol"; import { PackedCounter } from "@latticexyz/store/src/PackedCounter.sol"; -import { Counter, Position, PositionTableId, Orientation, EntityType, OwnedBy, EntityCustomization, GameSetting } from "../codegen/index.sol"; +import { Counter, Position, PositionTableId, Orientation, EntityType, OwnedBy, EntityCustomization, GameSetting, EntityTypeDetail } from "../codegen/index.sol"; import { positionToEntityKey } from "../positionToEntityKey.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; @@ -38,6 +38,13 @@ contract FacilitySystem is System { return keysAtPosition.length == 0; } + function facilitySystemSetupEntityTypeDetails() public { + EntityTypeDetail.set(1, 400, false); + EntityTypeDetail.set(102, 300, false); + EntityTypeDetail.set(103, 75, true); + EntityTypeDetail.set(104, 50, false); + } + /** * @dev Check if a player can build a facility of entityTypeId. * @param player The address of the player to check. @@ -45,17 +52,22 @@ contract FacilitySystem is System { * @return True if the player can build a facility of entityTypeId, false otherwise. */ function canPlayerBuildFacilityType(address player, uint32 entityTypeId) public view returns (bool) { - //TODO: require player to have enough resources to build this facility - - //For now, simply require player to have 10 LAPU to build any facility + uint256 buildingCostLapu = EntityTypeDetail.getBuildingCostLapu(entityTypeId); IERC20 lapu = IERC20(GameSetting.getLapuVaultAddress()); - return lapu.balanceOf(player) >= 10; + return lapu.balanceOf(player) >= buildingCostLapu; + } + + function lapuCostToBuildFacilityType(uint32 entityTypeId) public view returns (uint256) { + uint256 buildingCostLapu = EntityTypeDetail.getBuildingCostLapu(entityTypeId); + if (buildingCostLapu > 0) return buildingCostLapu; + else return 100; } function consumeResourcesToBuildFacilityType(address consumer, uint32 entityTypeId) public { - //transfer 10 LAPU from consumer to this world contract + //transfer buildingCostLapu LAPU from consumer to this world contract + uint256 buildingCostLapu = lapuCostToBuildFacilityType(entityTypeId); IERC20 lapu = IERC20(GameSetting.getLapuVaultAddress()); - SafeERC20.safeTransferFrom(lapu, consumer, address(this), 10); + SafeERC20.safeTransferFrom(lapu, consumer, address(this), buildingCostLapu); } /** From d06233217132b86ea5ea184bf77722b5d96a8239 Mon Sep 17 00:00:00 2001 From: Sing <16409791+singyiu@users.noreply.github.com> Date: Sat, 21 Oct 2023 08:07:39 -0700 Subject: [PATCH 3/9] feat: updatePlayerDataDetailForBuildingFacilityType --- packages/contracts/mud.config.ts | 2 +- .../contracts/src/systems/FacilitySystem.sol | 30 +++++++++++++++---- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/packages/contracts/mud.config.ts b/packages/contracts/mud.config.ts index 3aa11a2..1dd331e 100644 --- a/packages/contracts/mud.config.ts +++ b/packages/contracts/mud.config.ts @@ -46,7 +46,7 @@ export default mudConfig({ }, valueSchema: { buildingCostLapu: "uint256", - isResidence: "bool", + residence: "uint256", }, }, PlayerDataDetail: { diff --git a/packages/contracts/src/systems/FacilitySystem.sol b/packages/contracts/src/systems/FacilitySystem.sol index 98c4fd7..08a3ad9 100644 --- a/packages/contracts/src/systems/FacilitySystem.sol +++ b/packages/contracts/src/systems/FacilitySystem.sol @@ -7,7 +7,7 @@ import { getKeysWithValue } from "@latticexyz/world-modules/src/modules/keyswith import { getKeysInTable } from "@latticexyz/world-modules/src/modules/keysintable/getKeysInTable.sol"; import { PackedCounter } from "@latticexyz/store/src/PackedCounter.sol"; -import { Counter, Position, PositionTableId, Orientation, EntityType, OwnedBy, EntityCustomization, GameSetting, EntityTypeDetail } from "../codegen/index.sol"; +import { Counter, Position, PositionTableId, Orientation, EntityType, OwnedBy, EntityCustomization, GameSetting, EntityTypeDetail, PlayerDataDetail } from "../codegen/index.sol"; import { positionToEntityKey } from "../positionToEntityKey.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; @@ -39,10 +39,10 @@ contract FacilitySystem is System { } function facilitySystemSetupEntityTypeDetails() public { - EntityTypeDetail.set(1, 400, false); - EntityTypeDetail.set(102, 300, false); - EntityTypeDetail.set(103, 75, true); - EntityTypeDetail.set(104, 50, false); + EntityTypeDetail.set(1, 400, 0); + EntityTypeDetail.set(102, 300, 0); + EntityTypeDetail.set(103, 75, 1); + EntityTypeDetail.set(104, 50, 0); } /** @@ -70,6 +70,22 @@ contract FacilitySystem is System { SafeERC20.safeTransferFrom(lapu, consumer, address(this), buildingCostLapu); } + function updatePlayerDataDetailForBuildingFacilityType(address player, uint32 entityTypeId) public { + uint256 residence = EntityTypeDetail.getResidence(entityTypeId); + if (residence > 0) { + GameSetting.setTotalResidence(GameSetting.getTotalResidence() + residence); + PlayerDataDetail.setResidence(player, PlayerDataDetail.getResidence(player) + residence); + } + } + + function updatePlayerDataDetailForDestroyFacilityType(address player, uint32 entityTypeId) public { + uint256 residence = EntityTypeDetail.getResidence(entityTypeId); + if (residence > 0) { + GameSetting.setTotalResidence(GameSetting.getTotalResidence() - residence); + PlayerDataDetail.setResidence(player, PlayerDataDetail.getResidence(player) - residence); + } + } + /** * @dev Check if a position is next to an existing entity. * @param x The x coordinate of the position to check. @@ -134,6 +150,8 @@ contract FacilitySystem is System { EntityCustomization.set(entityKey, variant, color); OwnedBy.set(entityKey, _msgSender()); + updatePlayerDataDetailForBuildingFacilityType(_msgSender(), entityTypeId); + return entityKey; } @@ -145,6 +163,8 @@ contract FacilitySystem is System { require(_msgSender() != address(0), "Invalid sender address"); require(OwnedBy.get(entityKey) == _msgSender(), "Sender does not own this entity"); + updatePlayerDataDetailForDestroyFacilityType(_msgSender(), EntityType.get(entityKey)); + OwnedBy.deleteRecord(entityKey); EntityType.deleteRecord(entityKey); Orientation.deleteRecord(entityKey); From e39e419e332e1b827c1440790293ccbac686e700 Mon Sep 17 00:00:00 2001 From: Sing <16409791+singyiu@users.noreply.github.com> Date: Sat, 21 Oct 2023 11:29:22 -0700 Subject: [PATCH 4/9] feat: defiDistributeRewardsToPlayers --- packages/contracts/mud.config.ts | 10 +++--- packages/contracts/src/systems/DefiSystem.sol | 35 ++++++++++++++++++- .../contracts/src/systems/FacilitySystem.sol | 7 ++-- packages/contracts/src/systems/MockSystem.sol | 7 ++-- packages/contracts/test/DefiSystemtTest.t.sol | 30 ++++++++++------ 5 files changed, 68 insertions(+), 21 deletions(-) diff --git a/packages/contracts/mud.config.ts b/packages/contracts/mud.config.ts index 1dd331e..f654717 100644 --- a/packages/contracts/mud.config.ts +++ b/packages/contracts/mud.config.ts @@ -50,9 +50,6 @@ export default mudConfig({ }, }, PlayerDataDetail: { - keySchema: { - playerId: "address", - }, valueSchema: { residence: "uint256", rewarded: "uint256", @@ -68,12 +65,17 @@ export default mudConfig({ { name: "KeysInTableModule", root: true, - args: [resolveTableId("Position"), resolveTableId("PlayerDataDetail")], + args: [resolveTableId("Position")], }, { name: "KeysWithValueModule", root: true, args: [resolveTableId("Position")], }, + { + name: "KeysInTableModule", + root: true, + args: [resolveTableId("PlayerDataDetail")], + }, ], }); diff --git a/packages/contracts/src/systems/DefiSystem.sol b/packages/contracts/src/systems/DefiSystem.sol index 5d8ad2e..3214a84 100644 --- a/packages/contracts/src/systems/DefiSystem.sol +++ b/packages/contracts/src/systems/DefiSystem.sol @@ -2,7 +2,8 @@ pragma solidity >=0.8.21; import { System } from "@latticexyz/world/src/System.sol"; -import { GameSetting } from "../codegen/index.sol"; +import { GameSetting, PlayerDataDetail, PlayerDataDetailTableId } from "../codegen/index.sol"; +import { getKeysInTable } from "@latticexyz/world-modules/src/modules/keysintable/getKeysInTable.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; @@ -61,4 +62,36 @@ contract DefiSystem is System { ILapuVault lapuVault = ILapuVault(GameSetting.getLapuVaultAddress()); SafeERC20.safeTransferFrom(IERC20(lapuVault), consumer, address(this), amount); } + + //TODO: implement a better solution than looping through all players + function defiDistributeRewardsToPlayers() public returns (uint256) { + uint256 totalAmount = defiGetTotalRewardBalance(); + if (totalAmount == 0) return 0; + + uint256 totalResidence = GameSetting.getTotalResidence(); + if (totalResidence == 0) return 0; + + ILapuVault lapuVault = ILapuVault(GameSetting.getLapuVaultAddress()); + + bytes32[][] memory playerKeyss = getKeysInTable(PlayerDataDetailTableId); + + for (uint256 i = 0; i < playerKeyss.length; i++) { + bytes32[] memory playerKeys = playerKeyss[i]; + if (playerKeys.length > 0) { + bytes32 playerKey = playerKeys[0]; + address playerAddress = address(bytes20(playerKey)); + + uint256 playerResidence = PlayerDataDetail.getResidence(playerKey); + if (playerResidence == 0) continue; + + uint256 amount = (totalAmount * playerResidence) / totalResidence; + + if (amount > 0) { + lapuVault.transfer(playerAddress, amount); + PlayerDataDetail.setRewarded(playerKey, PlayerDataDetail.getRewarded(playerKey) + amount); + GameSetting.setTotalRewarded(GameSetting.getTotalRewarded() + amount); + } + } + } + } } diff --git a/packages/contracts/src/systems/FacilitySystem.sol b/packages/contracts/src/systems/FacilitySystem.sol index 08a3ad9..e484832 100644 --- a/packages/contracts/src/systems/FacilitySystem.sol +++ b/packages/contracts/src/systems/FacilitySystem.sol @@ -72,17 +72,20 @@ contract FacilitySystem is System { function updatePlayerDataDetailForBuildingFacilityType(address player, uint32 entityTypeId) public { uint256 residence = EntityTypeDetail.getResidence(entityTypeId); + bytes32 playerKey = bytes32(bytes20(player)); if (residence > 0) { GameSetting.setTotalResidence(GameSetting.getTotalResidence() + residence); - PlayerDataDetail.setResidence(player, PlayerDataDetail.getResidence(player) + residence); + //PlayerDataDetail.setResidence(playerKey, PlayerDataDetail.getResidence(playerKey) + residence); + PlayerDataDetail.set(playerKey, 1, 0); } } function updatePlayerDataDetailForDestroyFacilityType(address player, uint32 entityTypeId) public { uint256 residence = EntityTypeDetail.getResidence(entityTypeId); + bytes32 playerKey = bytes32(bytes20(player)); if (residence > 0) { GameSetting.setTotalResidence(GameSetting.getTotalResidence() - residence); - PlayerDataDetail.setResidence(player, PlayerDataDetail.getResidence(player) - residence); + PlayerDataDetail.setResidence(playerKey, PlayerDataDetail.getResidence(playerKey) - residence); } } diff --git a/packages/contracts/src/systems/MockSystem.sol b/packages/contracts/src/systems/MockSystem.sol index 45e455f..2d738b7 100644 --- a/packages/contracts/src/systems/MockSystem.sol +++ b/packages/contracts/src/systems/MockSystem.sol @@ -2,7 +2,7 @@ pragma solidity >=0.8.21; import { System } from "@latticexyz/world/src/System.sol"; -import { GameSetting } from "../codegen/index.sol"; +import { GameSetting, PlayerDataDetail } from "../codegen/index.sol"; import "../interfaces/IMockERC20.sol"; import "../interfaces/ILapuVault.sol"; @@ -30,7 +30,8 @@ contract MockSystem is System { function mockReleaseRewardToPlayer(address account, uint256 amount) public { ILapuVault lapuVault = ILapuVault(GameSetting.getLapuVaultAddress()); lapuVault.transfer(account, amount); - uint256 currentTotalRewarded = GameSetting.getTotalRewarded(); - GameSetting.setTotalRewarded(currentTotalRewarded + amount); + bytes32 accountKey = bytes32(bytes20(account)); + PlayerDataDetail.setRewarded(accountKey, PlayerDataDetail.getRewarded(accountKey) + amount); + GameSetting.setTotalRewarded(GameSetting.getTotalRewarded() + amount); } } diff --git a/packages/contracts/test/DefiSystemtTest.t.sol b/packages/contracts/test/DefiSystemtTest.t.sol index 63a33c2..d4e3671 100644 --- a/packages/contracts/test/DefiSystemtTest.t.sol +++ b/packages/contracts/test/DefiSystemtTest.t.sol @@ -7,10 +7,12 @@ import { MudTest } from "@latticexyz/world/test/MudTest.t.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { IWorld } from "../src/codegen/world/IWorld.sol"; -import { GameSetting } from "../src/codegen/index.sol"; +import { GameSetting, PlayerDataDetail } from "../src/codegen/index.sol"; contract DefiSystemTest is MudTest { IWorld public world; + uint32 public constant entityTypeIdGroundLevel = 1; + uint32 public constant entityTypeIdResidence = 103; function setUp() public override { super.setUp(); @@ -28,8 +30,9 @@ contract DefiSystemTest is MudTest { function testDefiSystemCycle() public { address player01 = address(0x5E11E1); // random address + bytes32 player01Key = bytes32(bytes20(player01)); uint256 amount01 = 1000; - uint256 amount02 = 100; + uint256 amount02 = 475; uint256 amount03 = 30; IERC20 dai = IERC20(GameSetting.getDaiAddress()); @@ -46,28 +49,33 @@ contract DefiSystemTest is MudTest { assertEq(world.defiDaiBalanceOf(player01), 0); assertEq(world.defiLapuBalanceOf(player01), amount01); - //3. player consume LAPU in game, i.e. for building facilities - lapu.approve(address(world), amount02); - world.defiConsumesLapuFromPlayer(amount02, player01); + //3. player build facility and consume LAPU in game + vm.startPrank(player01); + lapu.approve(address(world), amount01); + world.buildFacility(entityTypeIdGroundLevel, 1, 0, 1, 0, "#ffffff", 0); + world.buildFacility(entityTypeIdResidence, 1, 1, 1, 0, "#ffffff", 0); + vm.stopPrank(); assertEq(world.defiLapuBalanceOf(player01), amount01 - amount02); + assertEq(GameSetting.getTotalResidence(), 1); + assertEq(PlayerDataDetail.getResidence(player01Key), 1); + //4. LAPU balance of this world contract = reward balance assertEq(world.defiGetTotalRewardBalance(), amount02); - vm.stopPrank(); //5. mock yield generation from DeFi pool (which we can call periodically call from client) world.mockYieldGenerationFromDeFiPool(amount03); assertEq(world.defiGetTotalRewardBalance(), amount02 + amount03); - //6. mock release reward to player - world.mockReleaseRewardToPlayer(player01, amount03); - assertEq(world.defiGetTotalRewardBalance(), amount02); - assertEq(world.defiLapuBalanceOf(player01), amount01 - amount02 + amount03); + //6. release rewards to players + world.defiDistributeRewardsToPlayers(); + assertEq(world.defiGetTotalRewardBalance(), 0); + assertEq(world.defiLapuBalanceOf(player01), amount01 + amount03); vm.startPrank(player01); //7. player can cash out LAPU to DAI lapu.approve(address(world), amount03); world.defiSwapLapuToDai(amount03, player01); - assertEq(world.defiLapuBalanceOf(player01), amount01 - amount02); + assertEq(world.defiLapuBalanceOf(player01), amount01); assertEq(world.defiDaiBalanceOf(player01), amount03); vm.stopPrank(); } From 43208df9e5539af3d54c4e71ab697c1c3652f1ec Mon Sep 17 00:00:00 2001 From: Sing <16409791+singyiu@users.noreply.github.com> Date: Sat, 21 Oct 2023 12:08:22 -0700 Subject: [PATCH 5/9] chore: cleanup --- packages/contracts/src/systems/FacilitySystem.sol | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/contracts/src/systems/FacilitySystem.sol b/packages/contracts/src/systems/FacilitySystem.sol index e484832..e07120d 100644 --- a/packages/contracts/src/systems/FacilitySystem.sol +++ b/packages/contracts/src/systems/FacilitySystem.sol @@ -75,8 +75,7 @@ contract FacilitySystem is System { bytes32 playerKey = bytes32(bytes20(player)); if (residence > 0) { GameSetting.setTotalResidence(GameSetting.getTotalResidence() + residence); - //PlayerDataDetail.setResidence(playerKey, PlayerDataDetail.getResidence(playerKey) + residence); - PlayerDataDetail.set(playerKey, 1, 0); + PlayerDataDetail.setResidence(playerKey, PlayerDataDetail.getResidence(playerKey) + residence); } } From 061e1ea389abd4183e3dbf912ba8819e35d8832b Mon Sep 17 00:00:00 2001 From: Sing <16409791+singyiu@users.noreply.github.com> Date: Sat, 21 Oct 2023 16:26:52 -0700 Subject: [PATCH 6/9] feat: mockLapuVaultFundPlayer --- .../client/src/components/ui/tutorialModal.tsx | 9 +++++++++ packages/client/src/mud/createSystemCalls.ts | 16 ++++++++++++++++ packages/client/src/mudExample.tsx | 10 ++++++---- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/packages/client/src/components/ui/tutorialModal.tsx b/packages/client/src/components/ui/tutorialModal.tsx index 9681620..05ba45c 100644 --- a/packages/client/src/components/ui/tutorialModal.tsx +++ b/packages/client/src/components/ui/tutorialModal.tsx @@ -4,6 +4,8 @@ import { getState } from "@/game/store"; import { useState, useEffect } from "react"; import { useSpring, animated, config } from "@react-spring/web"; +import { useMUD } from "@/useMUD"; + function TutorialModal({ step, screenIndex, @@ -75,6 +77,11 @@ function TutorialModal({ } const Tutorial = () => { + const { + systemCalls: { mockLapuVaultFundPlayer }, + } = useMUD(); + const playerAddress = getState().player?.playerData?.address; + const [currentTutorial, setCurrentTutorial] = useState< TutorialStep | undefined >(undefined); @@ -119,6 +126,8 @@ const Tutorial = () => { } else { setScreenIndex(screenIndex + 1); } + + mockLapuVaultFundPlayer(playerAddress); }} /> )} diff --git a/packages/client/src/mud/createSystemCalls.ts b/packages/client/src/mud/createSystemCalls.ts index 07d4a35..70e4b92 100644 --- a/packages/client/src/mud/createSystemCalls.ts +++ b/packages/client/src/mud/createSystemCalls.ts @@ -240,6 +240,11 @@ export function createSystemCalls( const depositDaiToLapuVaultForTheConnectedPlayer = async (amount) => { const gameSetting = await getComponentValue(GameSetting, singletonEntity); + console.log("gameSetting?.lapuVaultAddress", gameSetting?.lapuVaultAddress); + console.log( + "depositDaiToLapuVaultForTheConnectedPlayer walletClient?.account.address", + walletClient?.account.address + ); const { request } = await publicClient.simulateContract({ address: gameSetting?.lapuVaultAddress, abi: ILapuVaultAbi, @@ -304,6 +309,16 @@ export function createSystemCalls( return data; }; + const mockLapuVaultFundPlayer = async (playerAddress, amount = 1000) => { + console.log("mockLapuVaultFundPlayer start", playerAddress, amount); + await mudMockDaiFaucet(playerAddress, amount); + console.log("mudMockDaiFaucet done"); + await approveDaiToLapuVaultForTheConnectedPlayer(amount); + console.log("approveDaiToLapuVaultForTheConnectedPlayer done"); + await depositDaiToLapuVaultForTheConnectedPlayer(amount); + console.log("mockLapuVaultFundPlayer done", playerAddress, amount); + }; + return { increment, mudGetEntityType, @@ -327,5 +342,6 @@ export function createSystemCalls( mudMockYieldGenerationFromDeFiPool, mudMockReleaseRewardToPlayer, lapuVaultGetTotalSupply, + mockLapuVaultFundPlayer, }; } diff --git a/packages/client/src/mudExample.tsx b/packages/client/src/mudExample.tsx index e486910..ed19c79 100644 --- a/packages/client/src/mudExample.tsx +++ b/packages/client/src/mudExample.tsx @@ -19,16 +19,16 @@ export const MudExample = () => { mudDefiLapuBalanceOf, mudDefiConsumesLapuFromPlayer, mudDefiGetTotalRewardBalance, - mudMockYieldGenerationFromDeFiPool, - mudMockReleaseRewardToPlayer, + //mudMockYieldGenerationFromDeFiPool, + //mudMockReleaseRewardToPlayer, lapuVaultGetTotalSupply, }, } = useMUD(); const defaultTestAmount = 1000; const defaultConsumeAmount = 100; - const defaultYieldAmount = 50; - const defaultRewardAmount = 30; + //const defaultYieldAmount = 50; + //const defaultRewardAmount = 30; const counter = useComponentValue(Counter, singletonEntity); const gameSetting = useComponentValue(GameSetting, singletonEntity); @@ -74,6 +74,7 @@ export const MudExample = () => { lapuVaultGetTotalSupply, ]); + /* useEffect(() => { const generateYieldIntervalId = setInterval(() => { mudMockYieldGenerationFromDeFiPool(defaultYieldAmount); @@ -91,6 +92,7 @@ export const MudExample = () => { clearInterval(rewardPlayerIntervalId); }; }, [playerAddress, mudMockReleaseRewardToPlayer]); + */ const delay = async (ms) => { return new Promise((resolve) => setTimeout(resolve, ms)); From 2785b2fd1e6bd0a1fa44aad63ead6020eb429047 Mon Sep 17 00:00:00 2001 From: Sing <16409791+singyiu@users.noreply.github.com> Date: Sat, 21 Oct 2023 18:41:26 -0700 Subject: [PATCH 7/9] feat: executeBackgroundTx --- packages/client/src/mud/createSystemCalls.ts | 56 +++++++++++++- packages/client/src/mudExample.tsx | 81 ++++++++++---------- 2 files changed, 94 insertions(+), 43 deletions(-) diff --git a/packages/client/src/mud/createSystemCalls.ts b/packages/client/src/mud/createSystemCalls.ts index 70e4b92..829bb1c 100644 --- a/packages/client/src/mud/createSystemCalls.ts +++ b/packages/client/src/mud/createSystemCalls.ts @@ -48,10 +48,15 @@ export function createSystemCalls( EntityType, OwnedBy, GameSetting, + EntityTypeDetail, }: ClientComponents ) { const defaultVector3 = new Vector3(1, 0, 3); + const delay = async (ms) => { + return new Promise((resolve) => setTimeout(resolve, ms)); + }; + const increment = async () => { /* * Because IncrementSystem @@ -92,6 +97,14 @@ export function createSystemCalls( return res; }; + const mudGetLapuBuildingCost = async (entityTypeId: number) => { + const entityTypeDetail = await getComponentValue( + EntityTypeDetail, + entityTypeId + ); + return entityTypeDetail?.buildingCostLapu || 400; + }; + const mudBuildFacility = async ( entityTypeId: number = 10, x: number = defaultVector3.x, @@ -101,6 +114,18 @@ export function createSystemCalls( color: string = "#ff00ff", variant: number = 0 ) => { + //approve mud world to spend LAPU for building + const buildingCostLapu = await mudGetLapuBuildingCost(entityTypeId); + console.log( + "mudBuildFacility approveLapuToMudWorldForTheConnectedPlayer", + buildingCostLapu + ); + await approveLapuToMudWorldForTheConnectedPlayer(buildingCostLapu); + console.log( + "mudBuildFacility approveLapuToMudWorldForTheConnectedPlayer done" + ); + + delay(1000); const tx = await worldContract.write.buildFacility([ entityTypeId, x, @@ -222,7 +247,10 @@ export function createSystemCalls( account: walletClient?.account, }); const res = await walletClient.writeContract(request); - return res; + const transaction = await publicClient.waitForTransactionReceipt({ + hash: res, + }); + return transaction; }; const approveLapuToMudWorldForTheConnectedPlayer = async (amount) => { @@ -235,7 +263,10 @@ export function createSystemCalls( account: walletClient?.account, }); const res = await walletClient.writeContract(request); - return res; + const transaction = await publicClient.waitForTransactionReceipt({ + hash: res, + }); + return transaction; }; const depositDaiToLapuVaultForTheConnectedPlayer = async (amount) => { @@ -253,7 +284,10 @@ export function createSystemCalls( account: walletClient?.account, }); const res = await walletClient.writeContract(request); - return res; + const transaction = await publicClient.waitForTransactionReceipt({ + hash: res, + }); + return transaction; }; const withdrawDaiFromLapuVaultForTheConnectedPlayer = async (amount) => { @@ -270,7 +304,10 @@ export function createSystemCalls( account: walletClient?.account, }); const res = await walletClient.writeContract(request); - return res; + const transaction = await publicClient.waitForTransactionReceipt({ + hash: res, + }); + return transaction; }; const mudDefiConsumesLapuFromPlayer = async (amount, playerAddress) => { @@ -283,6 +320,7 @@ export function createSystemCalls( }; const mudMockYieldGenerationFromDeFiPool = async (amount) => { + console.log("mudMockYieldGenerationFromDeFiPool", amount); const tx = await worldContract.write.mockYieldGenerationFromDeFiPool([ amount, ]); @@ -313,12 +351,21 @@ export function createSystemCalls( console.log("mockLapuVaultFundPlayer start", playerAddress, amount); await mudMockDaiFaucet(playerAddress, amount); console.log("mudMockDaiFaucet done"); + delay(1000); await approveDaiToLapuVaultForTheConnectedPlayer(amount); console.log("approveDaiToLapuVaultForTheConnectedPlayer done"); + delay(1000); await depositDaiToLapuVaultForTheConnectedPlayer(amount); console.log("mockLapuVaultFundPlayer done", playerAddress, amount); }; + const mudDefiDistributeRewardsToPlayers = async () => { + console.log("mudDefiDistributeRewardsToPlayers"); + const tx = await worldContract.write.defiDistributeRewardsToPlayers(); + await waitForTransaction(tx); + return tx; + }; + return { increment, mudGetEntityType, @@ -343,5 +390,6 @@ export function createSystemCalls( mudMockReleaseRewardToPlayer, lapuVaultGetTotalSupply, mockLapuVaultFundPlayer, + mudDefiDistributeRewardsToPlayers, }; } diff --git a/packages/client/src/mudExample.tsx b/packages/client/src/mudExample.tsx index ed19c79..8599e26 100644 --- a/packages/client/src/mudExample.tsx +++ b/packages/client/src/mudExample.tsx @@ -11,24 +11,22 @@ export const MudExample = () => { components: { Counter, GameSetting }, systemCalls: { mudDefiDaiBalanceOf, - mudMockDaiFaucet, approveDaiToLapuVaultForTheConnectedPlayer, approveLapuToMudWorldForTheConnectedPlayer, depositDaiToLapuVaultForTheConnectedPlayer, withdrawDaiFromLapuVaultForTheConnectedPlayer, mudDefiLapuBalanceOf, - mudDefiConsumesLapuFromPlayer, mudDefiGetTotalRewardBalance, - //mudMockYieldGenerationFromDeFiPool, - //mudMockReleaseRewardToPlayer, + mudMockYieldGenerationFromDeFiPool, lapuVaultGetTotalSupply, + mockLapuVaultFundPlayer, + mudDefiDistributeRewardsToPlayers, }, } = useMUD(); const defaultTestAmount = 1000; - const defaultConsumeAmount = 100; - //const defaultYieldAmount = 50; - //const defaultRewardAmount = 30; + const defaultConsumeAmount = 400; + const defaultYieldAmount = 50; const counter = useComponentValue(Counter, singletonEntity); const gameSetting = useComponentValue(GameSetting, singletonEntity); @@ -41,6 +39,8 @@ export const MudExample = () => { null ); const [lapuVaultTvl, setLapuVaultTvl] = useState(0); + const [backgroundTxEnabled, setBackgroundTxEnabled] = + useState(false); useEffect(() => { const refreshData = async () => { @@ -62,7 +62,7 @@ export const MudExample = () => { setLapuVaultTvl(lapuVaultTvl_); }; - const refreshDataIntervalId = setInterval(refreshData, 1000); + const refreshDataIntervalId = setInterval(refreshData, 3000); return () => { clearInterval(refreshDataIntervalId); }; @@ -74,25 +74,30 @@ export const MudExample = () => { lapuVaultGetTotalSupply, ]); - /* useEffect(() => { - const generateYieldIntervalId = setInterval(() => { - mudMockYieldGenerationFromDeFiPool(defaultYieldAmount); - }, 10000); - return () => { - clearInterval(generateYieldIntervalId); + const executeBackgroundTx = async () => { + const rewardBalance = (await mudDefiGetTotalRewardBalance()) as number; + if (rewardBalance > 0) { + await mudDefiDistributeRewardsToPlayers(); + } else { + await mudMockYieldGenerationFromDeFiPool(defaultYieldAmount); + } }; - }, [mudMockYieldGenerationFromDeFiPool]); - useEffect(() => { - const rewardPlayerIntervalId = setInterval(() => { - mudMockReleaseRewardToPlayer(playerAddress, defaultRewardAmount); - }, 15000); + const backgroundTxIntervalId = setInterval(() => { + if (backgroundTxEnabled) { + executeBackgroundTx(); + } + }, 30000); return () => { - clearInterval(rewardPlayerIntervalId); + clearInterval(backgroundTxIntervalId); }; - }, [playerAddress, mudMockReleaseRewardToPlayer]); - */ + }, [ + backgroundTxEnabled, + mudDefiGetTotalRewardBalance, + mudMockYieldGenerationFromDeFiPool, + mudDefiDistributeRewardsToPlayers, + ]); const delay = async (ms) => { return new Promise((resolve) => setTimeout(resolve, ms)); @@ -125,17 +130,12 @@ export const MudExample = () => { onClick={async (event) => { event.preventDefault(); console.log( - "mudMockDaiFaucet:", - await mudMockDaiFaucet(playerAddress, defaultTestAmount) + "mockLapuVaultFundPlayer:", + await mockLapuVaultFundPlayer(playerAddress) ); - const playerDaiBalance_ = (await mudDefiDaiBalanceOf( - playerAddress - )) as number; - console.log("playerDaiBalance_:", playerDaiBalance_); - setPlayerDaiBalance(playerDaiBalance_); }} > - getDaiFromFaucet + FundPlayer + ); }; From c186f8ab5d4895388593b49048112f5b028e604b Mon Sep 17 00:00:00 2001 From: Sing <16409791+singyiu@users.noreply.github.com> Date: Sat, 21 Oct 2023 19:01:34 -0700 Subject: [PATCH 8/9] feat: backgroundTxIntervalTime --- packages/client/src/mudExample.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/client/src/mudExample.tsx b/packages/client/src/mudExample.tsx index 8599e26..387db45 100644 --- a/packages/client/src/mudExample.tsx +++ b/packages/client/src/mudExample.tsx @@ -27,6 +27,7 @@ export const MudExample = () => { const defaultTestAmount = 1000; const defaultConsumeAmount = 400; const defaultYieldAmount = 50; + const backgroundTxIntervalTime = 20000; const counter = useComponentValue(Counter, singletonEntity); const gameSetting = useComponentValue(GameSetting, singletonEntity); @@ -88,7 +89,7 @@ export const MudExample = () => { if (backgroundTxEnabled) { executeBackgroundTx(); } - }, 30000); + }, backgroundTxIntervalTime); return () => { clearInterval(backgroundTxIntervalId); }; From 46be15f6854757585341ead8b6741d48bd5601f2 Mon Sep 17 00:00:00 2001 From: Sing <16409791+singyiu@users.noreply.github.com> Date: Sat, 21 Oct 2023 19:14:55 -0700 Subject: [PATCH 9/9] feat: deployed to 4242 --- packages/contracts/worlds.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/contracts/worlds.json b/packages/contracts/worlds.json index e598e98..b122bf1 100644 --- a/packages/contracts/worlds.json +++ b/packages/contracts/worlds.json @@ -1,7 +1,7 @@ { "4242": { - "address": "0xceF3b131C2568231907d8Ce5D592773642bB60ed", - "blockNumber": 28118760 + "address": "0x79A093E913C12bee8099232aDcF9F445a2f074fF", + "blockNumber": 28247102 }, "31337": { "address": "0x5FbDB2315678afecb367f032d93F642f64180aa3"