From f2d79e03615e14e633ad139327be6cc40442d21b Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Fri, 15 Sep 2023 12:50:08 +0100 Subject: [PATCH 01/20] feat(world): add BatchCallModule --- packages/world/mud.config.ts | 1 + .../world/src/interfaces/IBatchCallSystem.sol | 6 ++ .../src/modules/batchcall/BatchCallModule.sol | 31 +++++++ .../src/modules/batchcall/BatchCallSystem.sol | 16 ++++ .../world/src/modules/batchcall/constants.sol | 6 ++ packages/world/test/BatchCallModule.t.sol | 89 +++++++++++++++++++ 6 files changed, 149 insertions(+) create mode 100644 packages/world/src/interfaces/IBatchCallSystem.sol create mode 100644 packages/world/src/modules/batchcall/BatchCallModule.sol create mode 100644 packages/world/src/modules/batchcall/BatchCallSystem.sol create mode 100644 packages/world/src/modules/batchcall/constants.sol create mode 100644 packages/world/test/BatchCallModule.t.sol diff --git a/packages/world/mud.config.ts b/packages/world/mud.config.ts index a043d644dd..3968f959db 100644 --- a/packages/world/mud.config.ts +++ b/packages/world/mud.config.ts @@ -200,6 +200,7 @@ export default mudConfig({ // TODO: Move optional modules into a separate package // (see https://github.com/latticexyz/mud/pull/584) "UniqueEntitySystem", + "BatchCallSystem", // Worldgen currently does not support systems inheriting logic // from other contracts, so all parts of CoreSystem are named diff --git a/packages/world/src/interfaces/IBatchCallSystem.sol b/packages/world/src/interfaces/IBatchCallSystem.sol new file mode 100644 index 0000000000..ec95752d02 --- /dev/null +++ b/packages/world/src/interfaces/IBatchCallSystem.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +interface IBatchCallSystem { + function batchCall_system_batchCall(bytes32[] memory resourceSelectors, bytes[] memory funcSelectorAndArgs) external; +} diff --git a/packages/world/src/modules/batchcall/BatchCallModule.sol b/packages/world/src/modules/batchcall/BatchCallModule.sol new file mode 100644 index 0000000000..767e08a767 --- /dev/null +++ b/packages/world/src/modules/batchcall/BatchCallModule.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import { IBaseWorld } from "../../interfaces/IBaseWorld.sol"; + +import { Module } from "../../Module.sol"; +import { ResourceSelector } from "../../ResourceSelector.sol"; + +import { BatchCallSystem } from "./BatchCallSystem.sol"; + +import { NAMESPACE, MODULE_NAME, SYSTEM_NAME } from "./constants.sol"; + +contract BatchCallModule is Module { + BatchCallSystem private immutable batchCallSystem = new BatchCallSystem(); + + function getName() public pure returns (bytes16) { + return MODULE_NAME; + } + + function installRoot(bytes memory args) public { + install(args); + } + + function install(bytes memory) public { + IBaseWorld world = IBaseWorld(_world()); + + world.registerSystem(ResourceSelector.from(NAMESPACE, SYSTEM_NAME), batchCallSystem, true); + + world.registerFunctionSelector(ResourceSelector.from(NAMESPACE, SYSTEM_NAME), "batchCall", "(bytes32[],bytes[])"); + } +} diff --git a/packages/world/src/modules/batchcall/BatchCallSystem.sol b/packages/world/src/modules/batchcall/BatchCallSystem.sol new file mode 100644 index 0000000000..1a856d9b64 --- /dev/null +++ b/packages/world/src/modules/batchcall/BatchCallSystem.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; +import { System } from "../../System.sol"; +import { IBaseWorld } from "../../interfaces/IBaseWorld.sol"; + +// Helper system to create and initialize matches in a single call +// Only the match creator can set access lists or register, so the world calls these on their behalf +contract BatchCallSystem is System { + function batchCall(bytes32[] memory resourceSelectors, bytes[] memory funcSelectorAndArgss) public { + IBaseWorld world = IBaseWorld(_world()); + + for (uint256 i; i < resourceSelectors.length; i++) { + world.callFrom(_msgSender(), resourceSelectors[i], funcSelectorAndArgss[i]); + } + } +} diff --git a/packages/world/src/modules/batchcall/constants.sol b/packages/world/src/modules/batchcall/constants.sol new file mode 100644 index 0000000000..ec8fdefb8b --- /dev/null +++ b/packages/world/src/modules/batchcall/constants.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +bytes16 constant NAMESPACE = bytes16("batchCall"); +bytes16 constant MODULE_NAME = bytes16("batchCall.m"); +bytes16 constant SYSTEM_NAME = bytes16("system"); diff --git a/packages/world/test/BatchCallModule.t.sol b/packages/world/test/BatchCallModule.t.sol new file mode 100644 index 0000000000..5131d428ab --- /dev/null +++ b/packages/world/test/BatchCallModule.t.sol @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import { Test } from "forge-std/Test.sol"; +import { GasReporter } from "@latticexyz/gas-report/src/GasReporter.sol"; + +import { ROOT_NAMESPACE, UNLIMITED_DELEGATION } from "../src/constants.sol"; +import { World } from "../src/World.sol"; +import { IBaseWorld } from "../src/interfaces/IBaseWorld.sol"; +import { IWorldErrors } from "../src/interfaces/IWorldErrors.sol"; + +import { CoreModule } from "../src/modules/core/CoreModule.sol"; +import { BatchCallModule } from "../src/modules/batchcall/BatchCallModule.sol"; +import { IBatchCallSystem } from "../src/interfaces/IBatchCallSystem.sol"; + +import { NAMESPACE } from "../src/modules/batchcall/constants.sol"; +import { ResourceSelector } from "../src/ResourceSelector.sol"; + +import { WorldTestSystem } from "./World.t.sol"; + +contract BatchCallModuleTest is Test, GasReporter { + using ResourceSelector for bytes32; + + IBaseWorld world; + BatchCallModule batchCallModule = new BatchCallModule(); + + function setUp() public { + world = IBaseWorld(address(new World())); + world.initialize(new CoreModule()); + } + + function testInstall() public { + startGasReport("install batch call module"); + world.installModule(batchCallModule, new bytes(0)); + endGasReport(); + + // Register a new system + WorldTestSystem system = new WorldTestSystem(); + bytes32 resourceSelector = ResourceSelector.from("namespace", "testSystem"); + + world.registerSystem(resourceSelector, system, false); + + bytes32[] memory resourceSelectors = new bytes32[](1); + bytes[] memory funcSelectorAndArgss = new bytes[](1); + + resourceSelectors[0] = resourceSelector; + funcSelectorAndArgss[0] = abi.encodeWithSelector(WorldTestSystem.getStoreAddress.selector); + + // TODO: do not hardcode this + address delegatee = 0x104fBc016F4bb334D775a19E8A6510109AC63E00; + + vm.expectRevert(abi.encodeWithSelector(IWorldErrors.DelegationNotFound.selector, address(this), delegatee)); + IBatchCallSystem(address(world)).batchCall_system_batchCall(resourceSelectors, funcSelectorAndArgss); + + // Register an unlimited delegation + world.registerDelegation(delegatee, UNLIMITED_DELEGATION, new bytes(0)); + + IBatchCallSystem(address(world)).batchCall_system_batchCall(resourceSelectors, funcSelectorAndArgss); + } + + function testInstallRoot() public { + startGasReport("installRoot batch call module"); + world.installRootModule(batchCallModule, new bytes(0)); + endGasReport(); + + // Register a new system + WorldTestSystem system = new WorldTestSystem(); + bytes32 resourceSelector = ResourceSelector.from("namespace", "testSystem"); + + world.registerSystem(resourceSelector, system, false); + + bytes32[] memory resourceSelectors = new bytes32[](1); + bytes[] memory funcSelectorAndArgss = new bytes[](1); + + resourceSelectors[0] = resourceSelector; + funcSelectorAndArgss[0] = abi.encodeWithSelector(WorldTestSystem.getStoreAddress.selector); + + // TODO: do not hardcode this + address delegatee = 0x104fBc016F4bb334D775a19E8A6510109AC63E00; + + vm.expectRevert(abi.encodeWithSelector(IWorldErrors.DelegationNotFound.selector, address(this), delegatee)); + IBatchCallSystem(address(world)).batchCall_system_batchCall(resourceSelectors, funcSelectorAndArgss); + + // Register an unlimited delegation + world.registerDelegation(delegatee, UNLIMITED_DELEGATION, new bytes(0)); + + IBatchCallSystem(address(world)).batchCall_system_batchCall(resourceSelectors, funcSelectorAndArgss); + } +} From e5bba6720d80f0e72a2e5c6872cce3cdcdd5a9bd Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Fri, 15 Sep 2023 12:51:52 +0100 Subject: [PATCH 02/20] fix: remove comment --- packages/world/src/modules/batchcall/BatchCallSystem.sol | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/world/src/modules/batchcall/BatchCallSystem.sol b/packages/world/src/modules/batchcall/BatchCallSystem.sol index 1a856d9b64..cd480103f0 100644 --- a/packages/world/src/modules/batchcall/BatchCallSystem.sol +++ b/packages/world/src/modules/batchcall/BatchCallSystem.sol @@ -3,8 +3,6 @@ pragma solidity >=0.8.0; import { System } from "../../System.sol"; import { IBaseWorld } from "../../interfaces/IBaseWorld.sol"; -// Helper system to create and initialize matches in a single call -// Only the match creator can set access lists or register, so the world calls these on their behalf contract BatchCallSystem is System { function batchCall(bytes32[] memory resourceSelectors, bytes[] memory funcSelectorAndArgss) public { IBaseWorld world = IBaseWorld(_world()); From 8ab8d0438b0453f83574576e5f79dfded94f1f87 Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Mon, 18 Sep 2023 11:52:07 +0100 Subject: [PATCH 03/20] chore: run gas report, rename to callDatas --- packages/world/gas-report.json | 24 ++++++++++++++++ .../src/modules/batchcall/BatchCallSystem.sol | 5 ++-- packages/world/test/BatchCallModule.t.sol | 28 ++++++++++++------- 3 files changed, 45 insertions(+), 12 deletions(-) diff --git a/packages/world/gas-report.json b/packages/world/gas-report.json index 24a569c163..a35b2e4a15 100644 --- a/packages/world/gas-report.json +++ b/packages/world/gas-report.json @@ -35,6 +35,30 @@ "name": "AccessControl: requireAccess (this address)", "gasUsed": 153 }, + { + "file": "test/BatchCallModule.t.sol", + "test": "testInstall", + "name": "install batch call module", + "gasUsed": 393054 + }, + { + "file": "test/BatchCallModule.t.sol", + "test": "testInstall", + "name": "batch calling (non-root module)", + "gasUsed": 47158 + }, + { + "file": "test/BatchCallModule.t.sol", + "test": "testInstallRoot", + "name": "installRoot batch call module", + "gasUsed": 383113 + }, + { + "file": "test/BatchCallModule.t.sol", + "test": "testInstallRoot", + "name": "batch calling (root module)", + "gasUsed": 47158 + }, { "file": "test/KeysInTableModule.t.sol", "test": "testInstallComposite", diff --git a/packages/world/src/modules/batchcall/BatchCallSystem.sol b/packages/world/src/modules/batchcall/BatchCallSystem.sol index cd480103f0..c653a697de 100644 --- a/packages/world/src/modules/batchcall/BatchCallSystem.sol +++ b/packages/world/src/modules/batchcall/BatchCallSystem.sol @@ -4,11 +4,12 @@ import { System } from "../../System.sol"; import { IBaseWorld } from "../../interfaces/IBaseWorld.sol"; contract BatchCallSystem is System { - function batchCall(bytes32[] memory resourceSelectors, bytes[] memory funcSelectorAndArgss) public { + function batchCall(bytes32[] memory resourceSelectors, bytes[] memory callDatas) public { IBaseWorld world = IBaseWorld(_world()); + address delegator = _msgSender(); for (uint256 i; i < resourceSelectors.length; i++) { - world.callFrom(_msgSender(), resourceSelectors[i], funcSelectorAndArgss[i]); + world.callFrom(delegator, resourceSelectors[i], callDatas[i]); } } } diff --git a/packages/world/test/BatchCallModule.t.sol b/packages/world/test/BatchCallModule.t.sol index 5131d428ab..dd05610f87 100644 --- a/packages/world/test/BatchCallModule.t.sol +++ b/packages/world/test/BatchCallModule.t.sol @@ -40,22 +40,26 @@ contract BatchCallModuleTest is Test, GasReporter { world.registerSystem(resourceSelector, system, false); - bytes32[] memory resourceSelectors = new bytes32[](1); - bytes[] memory funcSelectorAndArgss = new bytes[](1); + bytes32[] memory resourceSelectors = new bytes32[](2); + bytes[] memory callDatas = new bytes[](2); resourceSelectors[0] = resourceSelector; - funcSelectorAndArgss[0] = abi.encodeWithSelector(WorldTestSystem.getStoreAddress.selector); + callDatas[0] = abi.encodeWithSelector(WorldTestSystem.getStoreAddress.selector); + resourceSelectors[1] = resourceSelector; + callDatas[1] = abi.encodeWithSelector(WorldTestSystem.msgSender.selector); // TODO: do not hardcode this address delegatee = 0x104fBc016F4bb334D775a19E8A6510109AC63E00; vm.expectRevert(abi.encodeWithSelector(IWorldErrors.DelegationNotFound.selector, address(this), delegatee)); - IBatchCallSystem(address(world)).batchCall_system_batchCall(resourceSelectors, funcSelectorAndArgss); + IBatchCallSystem(address(world)).batchCall_system_batchCall(resourceSelectors, callDatas); // Register an unlimited delegation world.registerDelegation(delegatee, UNLIMITED_DELEGATION, new bytes(0)); - IBatchCallSystem(address(world)).batchCall_system_batchCall(resourceSelectors, funcSelectorAndArgss); + startGasReport("batch calling (non-root module)"); + IBatchCallSystem(address(world)).batchCall_system_batchCall(resourceSelectors, callDatas); + endGasReport(); } function testInstallRoot() public { @@ -69,21 +73,25 @@ contract BatchCallModuleTest is Test, GasReporter { world.registerSystem(resourceSelector, system, false); - bytes32[] memory resourceSelectors = new bytes32[](1); - bytes[] memory funcSelectorAndArgss = new bytes[](1); + bytes32[] memory resourceSelectors = new bytes32[](2); + bytes[] memory callDatas = new bytes[](2); resourceSelectors[0] = resourceSelector; - funcSelectorAndArgss[0] = abi.encodeWithSelector(WorldTestSystem.getStoreAddress.selector); + callDatas[0] = abi.encodeWithSelector(WorldTestSystem.getStoreAddress.selector); + resourceSelectors[1] = resourceSelector; + callDatas[1] = abi.encodeWithSelector(WorldTestSystem.msgSender.selector); // TODO: do not hardcode this address delegatee = 0x104fBc016F4bb334D775a19E8A6510109AC63E00; vm.expectRevert(abi.encodeWithSelector(IWorldErrors.DelegationNotFound.selector, address(this), delegatee)); - IBatchCallSystem(address(world)).batchCall_system_batchCall(resourceSelectors, funcSelectorAndArgss); + IBatchCallSystem(address(world)).batchCall_system_batchCall(resourceSelectors, callDatas); // Register an unlimited delegation world.registerDelegation(delegatee, UNLIMITED_DELEGATION, new bytes(0)); - IBatchCallSystem(address(world)).batchCall_system_batchCall(resourceSelectors, funcSelectorAndArgss); + startGasReport("batch calling (root module)"); + IBatchCallSystem(address(world)).batchCall_system_batchCall(resourceSelectors, callDatas); + endGasReport(); } } From 4f00381386b1f30dec02ac2ece86add4eb66cd65 Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Mon, 18 Sep 2023 12:54:09 +0100 Subject: [PATCH 04/20] feat: add batch call from root module --- packages/world/gas-report.json | 12 +++++ packages/world/src/interfaces/IBaseWorld.sol | 2 + .../src/interfaces/IBatchCallRootSystem.sol | 8 +++ .../batchcallroot/BatchCallRootModule.sol | 48 +++++++++++++++++ .../batchcallroot/BatchCallRootSystem.sol | 18 +++++++ .../src/modules/batchcallroot/constants.sol | 5 ++ packages/world/test/BatchCallRootModule.t.sol | 54 +++++++++++++++++++ 7 files changed, 147 insertions(+) create mode 100644 packages/world/src/interfaces/IBatchCallRootSystem.sol create mode 100644 packages/world/src/modules/batchcallroot/BatchCallRootModule.sol create mode 100644 packages/world/src/modules/batchcallroot/BatchCallRootSystem.sol create mode 100644 packages/world/src/modules/batchcallroot/constants.sol create mode 100644 packages/world/test/BatchCallRootModule.t.sol diff --git a/packages/world/gas-report.json b/packages/world/gas-report.json index a35b2e4a15..9cd81ad877 100644 --- a/packages/world/gas-report.json +++ b/packages/world/gas-report.json @@ -59,6 +59,18 @@ "name": "batch calling (root module)", "gasUsed": 47158 }, + { + "file": "test/BatchCallRootModule.t.sol", + "test": "testInstallRoot", + "name": "installRoot batch call root module", + "gasUsed": 1030959 + }, + { + "file": "test/BatchCallRootModule.t.sol", + "test": "testInstallRoot", + "name": "batch calling root (root module)", + "gasUsed": 41382 + }, { "file": "test/KeysInTableModule.t.sol", "test": "testInstallComposite", diff --git a/packages/world/src/interfaces/IBaseWorld.sol b/packages/world/src/interfaces/IBaseWorld.sol index dcc9607777..92e5304434 100644 --- a/packages/world/src/interfaces/IBaseWorld.sol +++ b/packages/world/src/interfaces/IBaseWorld.sol @@ -6,6 +6,7 @@ pragma solidity >=0.8.0; import { IStore } from "@latticexyz/store/src/IStore.sol"; import { IWorldKernel } from "../interfaces/IWorldKernel.sol"; +import { IBatchCallRootSystem } from "./IBatchCallRootSystem.sol"; import { ICoreSystem } from "./ICoreSystem.sol"; import { IAccessManagementSystem } from "./IAccessManagementSystem.sol"; import { IBalanceTransferSystem } from "./IBalanceTransferSystem.sol"; @@ -19,6 +20,7 @@ import { IWorldRegistrationSystem } from "./IWorldRegistrationSystem.sol"; interface IBaseWorld is IStore, IWorldKernel, + IBatchCallRootSystem, ICoreSystem, IAccessManagementSystem, IBalanceTransferSystem, diff --git a/packages/world/src/interfaces/IBatchCallRootSystem.sol b/packages/world/src/interfaces/IBatchCallRootSystem.sol new file mode 100644 index 0000000000..92da60481d --- /dev/null +++ b/packages/world/src/interfaces/IBatchCallRootSystem.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +/* Autogenerated file. Do not edit manually. */ + +interface IBatchCallRootSystem { + function batchCall(bytes32[] memory resourceSelectors, bytes[] memory callDatas) external; +} diff --git a/packages/world/src/modules/batchcallroot/BatchCallRootModule.sol b/packages/world/src/modules/batchcallroot/BatchCallRootModule.sol new file mode 100644 index 0000000000..ef61b38123 --- /dev/null +++ b/packages/world/src/modules/batchcallroot/BatchCallRootModule.sol @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import { IBaseWorld } from "../../interfaces/IBaseWorld.sol"; + +import { Module } from "../../Module.sol"; +import { ResourceSelector } from "../../ResourceSelector.sol"; +import { revertWithBytes } from "../../revertWithBytes.sol"; +import { ROOT_NAMESPACE } from "../../constants.sol"; + +import { BatchCallRootSystem } from "./BatchCallRootSystem.sol"; +import { MODULE_NAME, SYSTEM_NAME } from "./constants.sol"; + +contract BatchCallRootModule is Module { + BatchCallRootSystem private immutable batchCallRootSystem = new BatchCallRootSystem(); + + function getName() public pure returns (bytes16) { + return MODULE_NAME; + } + + function installRoot(bytes memory) public { + IBaseWorld world = IBaseWorld(_world()); + + (bool success, bytes memory returnData) = address(world).delegatecall( + abi.encodeCall( + world.registerSystem, + (ResourceSelector.from(ROOT_NAMESPACE, SYSTEM_NAME), batchCallRootSystem, true) + ) + ); + if (!success) revertWithBytes(returnData); + + (success, returnData) = address(world).delegatecall( + abi.encodeCall( + world.registerRootFunctionSelector, + ( + ResourceSelector.from(ROOT_NAMESPACE, SYSTEM_NAME), + BatchCallRootSystem.batchCall.selector, + BatchCallRootSystem.batchCall.selector + ) + ) + ); + if (!success) revertWithBytes(returnData); + } + + function install(bytes memory) public pure { + revert NonRootInstallNotSupported(); + } +} diff --git a/packages/world/src/modules/batchcallroot/BatchCallRootSystem.sol b/packages/world/src/modules/batchcallroot/BatchCallRootSystem.sol new file mode 100644 index 0000000000..7e42b21712 --- /dev/null +++ b/packages/world/src/modules/batchcallroot/BatchCallRootSystem.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; +import { System } from "../../System.sol"; +import { IBaseWorld } from "../../interfaces/IBaseWorld.sol"; +import { revertWithBytes } from "../../revertWithBytes.sol"; + +contract BatchCallRootSystem is System { + function batchCall(bytes32[] memory resourceSelectors, bytes[] memory callDatas) public { + IBaseWorld world = IBaseWorld(_world()); + + for (uint256 i; i < resourceSelectors.length; i++) { + (bool success, bytes memory returnData) = address(world).delegatecall( + abi.encodeCall(world.call, (resourceSelectors[i], callDatas[i])) + ); + if (!success) revertWithBytes(returnData); + } + } +} diff --git a/packages/world/src/modules/batchcallroot/constants.sol b/packages/world/src/modules/batchcallroot/constants.sol new file mode 100644 index 0000000000..cd13ff66c0 --- /dev/null +++ b/packages/world/src/modules/batchcallroot/constants.sol @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +bytes16 constant MODULE_NAME = bytes16("batchCall.m"); +bytes16 constant SYSTEM_NAME = bytes16("system"); diff --git a/packages/world/test/BatchCallRootModule.t.sol b/packages/world/test/BatchCallRootModule.t.sol new file mode 100644 index 0000000000..dceec45f31 --- /dev/null +++ b/packages/world/test/BatchCallRootModule.t.sol @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import { Test } from "forge-std/Test.sol"; +import { GasReporter } from "@latticexyz/gas-report/src/GasReporter.sol"; + +import { ROOT_NAMESPACE, UNLIMITED_DELEGATION } from "../src/constants.sol"; +import { World } from "../src/World.sol"; +import { IBaseWorld } from "../src/interfaces/IBaseWorld.sol"; +import { IWorldErrors } from "../src/interfaces/IWorldErrors.sol"; + +import { CoreModule } from "../src/modules/core/CoreModule.sol"; +import { BatchCallRootModule } from "../src/modules/batchcallroot/BatchCallRootModule.sol"; +import { IBatchCallRootSystem } from "../src/interfaces/IBatchCallRootSystem.sol"; + +import { NAMESPACE } from "../src/modules/batchcall/constants.sol"; +import { ResourceSelector } from "../src/ResourceSelector.sol"; + +import { WorldTestSystem } from "./World.t.sol"; + +contract BatchCallRootModuleTest is Test, GasReporter { + using ResourceSelector for bytes32; + + IBaseWorld world; + + function setUp() public { + world = IBaseWorld(address(new World())); + world.initialize(new CoreModule()); + } + + function testInstallRoot() public { + startGasReport("installRoot batch call root module"); + world.installRootModule(new BatchCallRootModule(), new bytes(0)); + endGasReport(); + + // Register a new system + WorldTestSystem system = new WorldTestSystem(); + bytes32 resourceSelector = ResourceSelector.from("namespace", "testSystem"); + + world.registerSystem(resourceSelector, system, false); + + bytes32[] memory resourceSelectors = new bytes32[](2); + bytes[] memory callDatas = new bytes[](2); + + resourceSelectors[0] = resourceSelector; + callDatas[0] = abi.encodeWithSelector(WorldTestSystem.getStoreAddress.selector); + resourceSelectors[1] = resourceSelector; + callDatas[1] = abi.encodeWithSelector(WorldTestSystem.msgSender.selector); + + startGasReport("batch calling root (root module)"); + world.batchCall(resourceSelectors, callDatas); + endGasReport(); + } +} From c3c3d0063d167342a0c01acae5e320127a99e20f Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Mon, 18 Sep 2023 13:45:57 +0100 Subject: [PATCH 05/20] chore: add batch call to default module contracts --- packages/cli/src/utils/modules/constants.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/cli/src/utils/modules/constants.ts b/packages/cli/src/utils/modules/constants.ts index ac84680fa2..987c32aa58 100644 --- a/packages/cli/src/utils/modules/constants.ts +++ b/packages/cli/src/utils/modules/constants.ts @@ -1,6 +1,7 @@ import KeysWithValueModuleData from "@latticexyz/world/out/KeysWithValueModule.sol/KeysWithValueModule.json" assert { type: "json" }; import KeysInTableModuleData from "@latticexyz/world/out/KeysInTableModule.sol/KeysInTableModule.json" assert { type: "json" }; import UniqueEntityModuleData from "@latticexyz/world/out/UniqueEntityModule.sol/UniqueEntityModule.json" assert { type: "json" }; +import BatchCallModuleData from "@latticexyz/world/out/BatchCallModule.sol/BatchCallModule.json" assert { type: "json" }; import { ContractCode } from "../utils/types"; // These modules are always deployed @@ -20,4 +21,9 @@ export const defaultModuleContracts: ContractCode[] = [ abi: UniqueEntityModuleData.abi, bytecode: UniqueEntityModuleData.bytecode, }, + { + name: "BatchCallModule", + abi: BatchCallModuleData.abi, + bytecode: BatchCallModuleData.bytecode, + }, ]; From ad2bebb20449a5e33871962db8f934ec51fbb3ab Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Mon, 18 Sep 2023 13:58:46 +0100 Subject: [PATCH 06/20] test: do not hardcode system address --- packages/world/test/BatchCallModule.t.sol | 54 +++---------------- packages/world/test/BatchCallRootModule.t.sol | 6 +-- 2 files changed, 9 insertions(+), 51 deletions(-) diff --git a/packages/world/test/BatchCallModule.t.sol b/packages/world/test/BatchCallModule.t.sol index dd05610f87..dcbba1564f 100644 --- a/packages/world/test/BatchCallModule.t.sol +++ b/packages/world/test/BatchCallModule.t.sol @@ -4,16 +4,17 @@ pragma solidity >=0.8.0; import { Test } from "forge-std/Test.sol"; import { GasReporter } from "@latticexyz/gas-report/src/GasReporter.sol"; -import { ROOT_NAMESPACE, UNLIMITED_DELEGATION } from "../src/constants.sol"; +import { UNLIMITED_DELEGATION } from "../src/constants.sol"; import { World } from "../src/World.sol"; import { IBaseWorld } from "../src/interfaces/IBaseWorld.sol"; import { IWorldErrors } from "../src/interfaces/IWorldErrors.sol"; +import { Systems } from "../src/modules/core/tables/Systems.sol"; import { CoreModule } from "../src/modules/core/CoreModule.sol"; import { BatchCallModule } from "../src/modules/batchcall/BatchCallModule.sol"; import { IBatchCallSystem } from "../src/interfaces/IBatchCallSystem.sol"; -import { NAMESPACE } from "../src/modules/batchcall/constants.sol"; +import { NAMESPACE, SYSTEM_NAME } from "../src/modules/batchcall/constants.sol"; import { ResourceSelector } from "../src/ResourceSelector.sol"; import { WorldTestSystem } from "./World.t.sol"; @@ -22,57 +23,19 @@ contract BatchCallModuleTest is Test, GasReporter { using ResourceSelector for bytes32; IBaseWorld world; - BatchCallModule batchCallModule = new BatchCallModule(); + bytes32 resourceSelector = ResourceSelector.from("namespace", "testSystem"); function setUp() public { world = IBaseWorld(address(new World())); world.initialize(new CoreModule()); - } - - function testInstall() public { - startGasReport("install batch call module"); - world.installModule(batchCallModule, new bytes(0)); - endGasReport(); + world.installModule(new BatchCallModule(), new bytes(0)); // Register a new system WorldTestSystem system = new WorldTestSystem(); - bytes32 resourceSelector = ResourceSelector.from("namespace", "testSystem"); - world.registerSystem(resourceSelector, system, false); - - bytes32[] memory resourceSelectors = new bytes32[](2); - bytes[] memory callDatas = new bytes[](2); - - resourceSelectors[0] = resourceSelector; - callDatas[0] = abi.encodeWithSelector(WorldTestSystem.getStoreAddress.selector); - resourceSelectors[1] = resourceSelector; - callDatas[1] = abi.encodeWithSelector(WorldTestSystem.msgSender.selector); - - // TODO: do not hardcode this - address delegatee = 0x104fBc016F4bb334D775a19E8A6510109AC63E00; - - vm.expectRevert(abi.encodeWithSelector(IWorldErrors.DelegationNotFound.selector, address(this), delegatee)); - IBatchCallSystem(address(world)).batchCall_system_batchCall(resourceSelectors, callDatas); - - // Register an unlimited delegation - world.registerDelegation(delegatee, UNLIMITED_DELEGATION, new bytes(0)); - - startGasReport("batch calling (non-root module)"); - IBatchCallSystem(address(world)).batchCall_system_batchCall(resourceSelectors, callDatas); - endGasReport(); } - function testInstallRoot() public { - startGasReport("installRoot batch call module"); - world.installRootModule(batchCallModule, new bytes(0)); - endGasReport(); - - // Register a new system - WorldTestSystem system = new WorldTestSystem(); - bytes32 resourceSelector = ResourceSelector.from("namespace", "testSystem"); - - world.registerSystem(resourceSelector, system, false); - + function testInstall() public { bytes32[] memory resourceSelectors = new bytes32[](2); bytes[] memory callDatas = new bytes[](2); @@ -81,8 +44,7 @@ contract BatchCallModuleTest is Test, GasReporter { resourceSelectors[1] = resourceSelector; callDatas[1] = abi.encodeWithSelector(WorldTestSystem.msgSender.selector); - // TODO: do not hardcode this - address delegatee = 0x104fBc016F4bb334D775a19E8A6510109AC63E00; + address delegatee = Systems.getSystem(world, ResourceSelector.from(NAMESPACE, SYSTEM_NAME)); vm.expectRevert(abi.encodeWithSelector(IWorldErrors.DelegationNotFound.selector, address(this), delegatee)); IBatchCallSystem(address(world)).batchCall_system_batchCall(resourceSelectors, callDatas); @@ -90,7 +52,7 @@ contract BatchCallModuleTest is Test, GasReporter { // Register an unlimited delegation world.registerDelegation(delegatee, UNLIMITED_DELEGATION, new bytes(0)); - startGasReport("batch calling (root module)"); + startGasReport("batch calling"); IBatchCallSystem(address(world)).batchCall_system_batchCall(resourceSelectors, callDatas); endGasReport(); } diff --git a/packages/world/test/BatchCallRootModule.t.sol b/packages/world/test/BatchCallRootModule.t.sol index dceec45f31..d12183f188 100644 --- a/packages/world/test/BatchCallRootModule.t.sol +++ b/packages/world/test/BatchCallRootModule.t.sol @@ -4,16 +4,12 @@ pragma solidity >=0.8.0; import { Test } from "forge-std/Test.sol"; import { GasReporter } from "@latticexyz/gas-report/src/GasReporter.sol"; -import { ROOT_NAMESPACE, UNLIMITED_DELEGATION } from "../src/constants.sol"; import { World } from "../src/World.sol"; import { IBaseWorld } from "../src/interfaces/IBaseWorld.sol"; -import { IWorldErrors } from "../src/interfaces/IWorldErrors.sol"; import { CoreModule } from "../src/modules/core/CoreModule.sol"; import { BatchCallRootModule } from "../src/modules/batchcallroot/BatchCallRootModule.sol"; -import { IBatchCallRootSystem } from "../src/interfaces/IBatchCallRootSystem.sol"; -import { NAMESPACE } from "../src/modules/batchcall/constants.sol"; import { ResourceSelector } from "../src/ResourceSelector.sol"; import { WorldTestSystem } from "./World.t.sol"; @@ -47,7 +43,7 @@ contract BatchCallRootModuleTest is Test, GasReporter { resourceSelectors[1] = resourceSelector; callDatas[1] = abi.encodeWithSelector(WorldTestSystem.msgSender.selector); - startGasReport("batch calling root (root module)"); + startGasReport("batch calling root"); world.batchCall(resourceSelectors, callDatas); endGasReport(); } From f7c9bc96608089ea142436675cf1602602bc7117 Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Mon, 18 Sep 2023 14:00:18 +0100 Subject: [PATCH 07/20] chore: run gas report --- packages/world/gas-report.json | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/packages/world/gas-report.json b/packages/world/gas-report.json index 9cd81ad877..26f2f4a594 100644 --- a/packages/world/gas-report.json +++ b/packages/world/gas-report.json @@ -38,26 +38,8 @@ { "file": "test/BatchCallModule.t.sol", "test": "testInstall", - "name": "install batch call module", - "gasUsed": 393054 - }, - { - "file": "test/BatchCallModule.t.sol", - "test": "testInstall", - "name": "batch calling (non-root module)", - "gasUsed": 47158 - }, - { - "file": "test/BatchCallModule.t.sol", - "test": "testInstallRoot", - "name": "installRoot batch call module", - "gasUsed": 383113 - }, - { - "file": "test/BatchCallModule.t.sol", - "test": "testInstallRoot", - "name": "batch calling (root module)", - "gasUsed": 47158 + "name": "batch calling", + "gasUsed": 60251 }, { "file": "test/BatchCallRootModule.t.sol", @@ -68,7 +50,7 @@ { "file": "test/BatchCallRootModule.t.sol", "test": "testInstallRoot", - "name": "batch calling root (root module)", + "name": "batch calling root", "gasUsed": 41382 }, { From c2a9869174c8b65c83b0b5055e637996d07f9cc8 Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Mon, 18 Sep 2023 20:35:31 +0100 Subject: [PATCH 08/20] chore: delete non-root batch call module --- packages/cli/src/utils/modules/constants.ts | 6 -- packages/world/gas-report.json | 28 ++++----- .../src/modules/batchcall/BatchCallModule.sol | 31 ---------- .../src/modules/batchcall/BatchCallSystem.sol | 15 ----- .../world/src/modules/batchcall/constants.sol | 6 -- packages/world/test/BatchCallModule.t.sol | 59 ------------------- 6 files changed, 11 insertions(+), 134 deletions(-) delete mode 100644 packages/world/src/modules/batchcall/BatchCallModule.sol delete mode 100644 packages/world/src/modules/batchcall/BatchCallSystem.sol delete mode 100644 packages/world/src/modules/batchcall/constants.sol delete mode 100644 packages/world/test/BatchCallModule.t.sol diff --git a/packages/cli/src/utils/modules/constants.ts b/packages/cli/src/utils/modules/constants.ts index 987c32aa58..ac84680fa2 100644 --- a/packages/cli/src/utils/modules/constants.ts +++ b/packages/cli/src/utils/modules/constants.ts @@ -1,7 +1,6 @@ import KeysWithValueModuleData from "@latticexyz/world/out/KeysWithValueModule.sol/KeysWithValueModule.json" assert { type: "json" }; import KeysInTableModuleData from "@latticexyz/world/out/KeysInTableModule.sol/KeysInTableModule.json" assert { type: "json" }; import UniqueEntityModuleData from "@latticexyz/world/out/UniqueEntityModule.sol/UniqueEntityModule.json" assert { type: "json" }; -import BatchCallModuleData from "@latticexyz/world/out/BatchCallModule.sol/BatchCallModule.json" assert { type: "json" }; import { ContractCode } from "../utils/types"; // These modules are always deployed @@ -21,9 +20,4 @@ export const defaultModuleContracts: ContractCode[] = [ abi: UniqueEntityModuleData.abi, bytecode: UniqueEntityModuleData.bytecode, }, - { - name: "BatchCallModule", - abi: BatchCallModuleData.abi, - bytecode: BatchCallModuleData.bytecode, - }, ]; diff --git a/packages/world/gas-report.json b/packages/world/gas-report.json index 0a8a17c3a8..103e145f94 100644 --- a/packages/world/gas-report.json +++ b/packages/world/gas-report.json @@ -35,23 +35,17 @@ "name": "AccessControl: requireAccess (this address)", "gasUsed": 153 }, - { - "file": "test/BatchCallModule.t.sol", - "test": "testInstall", - "name": "batch calling", - "gasUsed": 60251 - }, { "file": "test/BatchCallRootModule.t.sol", "test": "testInstallRoot", "name": "installRoot batch call root module", - "gasUsed": 1030959 + "gasUsed": 1030961 }, { "file": "test/BatchCallRootModule.t.sol", "test": "testInstallRoot", "name": "batch calling root", - "gasUsed": 41382 + "gasUsed": 41387 }, { "file": "test/KeysInTableModule.t.sol", @@ -297,19 +291,19 @@ "file": "test/World.t.sol", "test": "testCall", "name": "call a system via the World", - "gasUsed": 12380 + "gasUsed": 12333 }, { "file": "test/World.t.sol", "test": "testCallFromUnlimitedDelegation", "name": "register an unlimited delegation", - "gasUsed": 50260 + "gasUsed": 50259 }, { "file": "test/World.t.sol", "test": "testCallFromUnlimitedDelegation", "name": "call a system via an unlimited delegation", - "gasUsed": 12729 + "gasUsed": 12682 }, { "file": "test/World.t.sol", @@ -327,37 +321,37 @@ "file": "test/World.t.sol", "test": "testRegisterFallbackSystem", "name": "Register a fallback system", - "gasUsed": 58889 + "gasUsed": 58888 }, { "file": "test/World.t.sol", "test": "testRegisterFallbackSystem", "name": "Register a root fallback system", - "gasUsed": 52206 + "gasUsed": 52205 }, { "file": "test/World.t.sol", "test": "testRegisterFunctionSelector", "name": "Register a function selector", - "gasUsed": 79483 + "gasUsed": 79482 }, { "file": "test/World.t.sol", "test": "testRegisterNamespace", "name": "Register a new namespace", - "gasUsed": 122753 + "gasUsed": 122752 }, { "file": "test/World.t.sol", "test": "testRegisterRootFunctionSelector", "name": "Register a root function selector", - "gasUsed": 74124 + "gasUsed": 74123 }, { "file": "test/World.t.sol", "test": "testRegisterTable", "name": "Register a new table in the namespace", - "gasUsed": 641613 + "gasUsed": 641673 }, { "file": "test/World.t.sol", diff --git a/packages/world/src/modules/batchcall/BatchCallModule.sol b/packages/world/src/modules/batchcall/BatchCallModule.sol deleted file mode 100644 index 767e08a767..0000000000 --- a/packages/world/src/modules/batchcall/BatchCallModule.sol +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.8.0; - -import { IBaseWorld } from "../../interfaces/IBaseWorld.sol"; - -import { Module } from "../../Module.sol"; -import { ResourceSelector } from "../../ResourceSelector.sol"; - -import { BatchCallSystem } from "./BatchCallSystem.sol"; - -import { NAMESPACE, MODULE_NAME, SYSTEM_NAME } from "./constants.sol"; - -contract BatchCallModule is Module { - BatchCallSystem private immutable batchCallSystem = new BatchCallSystem(); - - function getName() public pure returns (bytes16) { - return MODULE_NAME; - } - - function installRoot(bytes memory args) public { - install(args); - } - - function install(bytes memory) public { - IBaseWorld world = IBaseWorld(_world()); - - world.registerSystem(ResourceSelector.from(NAMESPACE, SYSTEM_NAME), batchCallSystem, true); - - world.registerFunctionSelector(ResourceSelector.from(NAMESPACE, SYSTEM_NAME), "batchCall", "(bytes32[],bytes[])"); - } -} diff --git a/packages/world/src/modules/batchcall/BatchCallSystem.sol b/packages/world/src/modules/batchcall/BatchCallSystem.sol deleted file mode 100644 index c653a697de..0000000000 --- a/packages/world/src/modules/batchcall/BatchCallSystem.sol +++ /dev/null @@ -1,15 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.8.0; -import { System } from "../../System.sol"; -import { IBaseWorld } from "../../interfaces/IBaseWorld.sol"; - -contract BatchCallSystem is System { - function batchCall(bytes32[] memory resourceSelectors, bytes[] memory callDatas) public { - IBaseWorld world = IBaseWorld(_world()); - address delegator = _msgSender(); - - for (uint256 i; i < resourceSelectors.length; i++) { - world.callFrom(delegator, resourceSelectors[i], callDatas[i]); - } - } -} diff --git a/packages/world/src/modules/batchcall/constants.sol b/packages/world/src/modules/batchcall/constants.sol deleted file mode 100644 index ec8fdefb8b..0000000000 --- a/packages/world/src/modules/batchcall/constants.sol +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.8.0; - -bytes16 constant NAMESPACE = bytes16("batchCall"); -bytes16 constant MODULE_NAME = bytes16("batchCall.m"); -bytes16 constant SYSTEM_NAME = bytes16("system"); diff --git a/packages/world/test/BatchCallModule.t.sol b/packages/world/test/BatchCallModule.t.sol deleted file mode 100644 index dcbba1564f..0000000000 --- a/packages/world/test/BatchCallModule.t.sol +++ /dev/null @@ -1,59 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.8.0; - -import { Test } from "forge-std/Test.sol"; -import { GasReporter } from "@latticexyz/gas-report/src/GasReporter.sol"; - -import { UNLIMITED_DELEGATION } from "../src/constants.sol"; -import { World } from "../src/World.sol"; -import { IBaseWorld } from "../src/interfaces/IBaseWorld.sol"; -import { IWorldErrors } from "../src/interfaces/IWorldErrors.sol"; - -import { Systems } from "../src/modules/core/tables/Systems.sol"; -import { CoreModule } from "../src/modules/core/CoreModule.sol"; -import { BatchCallModule } from "../src/modules/batchcall/BatchCallModule.sol"; -import { IBatchCallSystem } from "../src/interfaces/IBatchCallSystem.sol"; - -import { NAMESPACE, SYSTEM_NAME } from "../src/modules/batchcall/constants.sol"; -import { ResourceSelector } from "../src/ResourceSelector.sol"; - -import { WorldTestSystem } from "./World.t.sol"; - -contract BatchCallModuleTest is Test, GasReporter { - using ResourceSelector for bytes32; - - IBaseWorld world; - bytes32 resourceSelector = ResourceSelector.from("namespace", "testSystem"); - - function setUp() public { - world = IBaseWorld(address(new World())); - world.initialize(new CoreModule()); - world.installModule(new BatchCallModule(), new bytes(0)); - - // Register a new system - WorldTestSystem system = new WorldTestSystem(); - world.registerSystem(resourceSelector, system, false); - } - - function testInstall() public { - bytes32[] memory resourceSelectors = new bytes32[](2); - bytes[] memory callDatas = new bytes[](2); - - resourceSelectors[0] = resourceSelector; - callDatas[0] = abi.encodeWithSelector(WorldTestSystem.getStoreAddress.selector); - resourceSelectors[1] = resourceSelector; - callDatas[1] = abi.encodeWithSelector(WorldTestSystem.msgSender.selector); - - address delegatee = Systems.getSystem(world, ResourceSelector.from(NAMESPACE, SYSTEM_NAME)); - - vm.expectRevert(abi.encodeWithSelector(IWorldErrors.DelegationNotFound.selector, address(this), delegatee)); - IBatchCallSystem(address(world)).batchCall_system_batchCall(resourceSelectors, callDatas); - - // Register an unlimited delegation - world.registerDelegation(delegatee, UNLIMITED_DELEGATION, new bytes(0)); - - startGasReport("batch calling"); - IBatchCallSystem(address(world)).batchCall_system_batchCall(resourceSelectors, callDatas); - endGasReport(); - } -} From 295ed8ce02ca28fc916ed339214f29247fee62a7 Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Mon, 18 Sep 2023 21:02:21 +0100 Subject: [PATCH 09/20] chore: remove mud config change --- packages/world/mud.config.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/world/mud.config.ts b/packages/world/mud.config.ts index 015ba8c99b..1ad535703d 100644 --- a/packages/world/mud.config.ts +++ b/packages/world/mud.config.ts @@ -195,7 +195,6 @@ export default mudConfig({ // TODO: Move optional modules into a separate package // (see https://github.com/latticexyz/mud/pull/584) "UniqueEntitySystem", - "BatchCallSystem", // Worldgen currently does not support systems inheriting logic // from other contracts, so all parts of CoreSystem are named From 7159e83957d61170c208c9f724930f7cb72e75d9 Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Mon, 18 Sep 2023 21:07:47 +0100 Subject: [PATCH 10/20] feat: rename to callBatch, add to core module --- packages/world/gas-report.json | 52 ++++++++----------- packages/world/src/interfaces/IBaseWorld.sol | 4 +- .../world/src/interfaces/IBatchCallSystem.sol | 6 --- ...allRootSystem.sol => ICallBatchSystem.sol} | 4 +- .../batchcallroot/BatchCallRootModule.sol | 48 ----------------- .../src/modules/batchcallroot/constants.sol | 5 -- .../world/src/modules/core/CoreModule.sol | 5 +- .../world/src/modules/core/CoreSystem.sol | 2 + .../implementations/CallBatchSystem.sol} | 13 +++-- ...chCallRootModule.t.sol => CallBatch.t.sol} | 11 ++-- 10 files changed, 44 insertions(+), 106 deletions(-) delete mode 100644 packages/world/src/interfaces/IBatchCallSystem.sol rename packages/world/src/interfaces/{IBatchCallRootSystem.sol => ICallBatchSystem.sol} (58%) delete mode 100644 packages/world/src/modules/batchcallroot/BatchCallRootModule.sol delete mode 100644 packages/world/src/modules/batchcallroot/constants.sol rename packages/world/src/modules/{batchcallroot/BatchCallRootSystem.sol => core/implementations/CallBatchSystem.sol} (52%) rename packages/world/test/{BatchCallRootModule.t.sol => CallBatch.t.sol} (77%) diff --git a/packages/world/gas-report.json b/packages/world/gas-report.json index 103e145f94..150cf4621a 100644 --- a/packages/world/gas-report.json +++ b/packages/world/gas-report.json @@ -36,28 +36,22 @@ "gasUsed": 153 }, { - "file": "test/BatchCallRootModule.t.sol", - "test": "testInstallRoot", - "name": "installRoot batch call root module", - "gasUsed": 1030961 - }, - { - "file": "test/BatchCallRootModule.t.sol", - "test": "testInstallRoot", + "file": "test/CallBatch.t.sol", + "test": "testCallBatch", "name": "batch calling root", - "gasUsed": 41387 + "gasUsed": 45672 }, { "file": "test/KeysInTableModule.t.sol", "test": "testInstallComposite", "name": "install keys in table module", - "gasUsed": 1415024 + "gasUsed": 1415164 }, { "file": "test/KeysInTableModule.t.sol", "test": "testInstallGas", "name": "install keys in table module", - "gasUsed": 1415024 + "gasUsed": 1415164 }, { "file": "test/KeysInTableModule.t.sol", @@ -69,13 +63,13 @@ "file": "test/KeysInTableModule.t.sol", "test": "testInstallSingleton", "name": "install keys in table module", - "gasUsed": 1415024 + "gasUsed": 1415164 }, { "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookCompositeGas", "name": "install keys in table module", - "gasUsed": 1415024 + "gasUsed": 1415164 }, { "file": "test/KeysInTableModule.t.sol", @@ -93,7 +87,7 @@ "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookGas", "name": "install keys in table module", - "gasUsed": 1415024 + "gasUsed": 1415164 }, { "file": "test/KeysInTableModule.t.sol", @@ -111,7 +105,7 @@ "file": "test/KeysWithValueModule.t.sol", "test": "testGetKeysWithValueGas", "name": "install keys with value module", - "gasUsed": 654245 + "gasUsed": 654315 }, { "file": "test/KeysWithValueModule.t.sol", @@ -129,7 +123,7 @@ "file": "test/KeysWithValueModule.t.sol", "test": "testInstall", "name": "install keys with value module", - "gasUsed": 654245 + "gasUsed": 654315 }, { "file": "test/KeysWithValueModule.t.sol", @@ -141,7 +135,7 @@ "file": "test/KeysWithValueModule.t.sol", "test": "testSetAndDeleteRecordHook", "name": "install keys with value module", - "gasUsed": 654245 + "gasUsed": 654315 }, { "file": "test/KeysWithValueModule.t.sol", @@ -159,7 +153,7 @@ "file": "test/KeysWithValueModule.t.sol", "test": "testSetField", "name": "install keys with value module", - "gasUsed": 654245 + "gasUsed": 654315 }, { "file": "test/KeysWithValueModule.t.sol", @@ -267,7 +261,7 @@ "file": "test/UniqueEntityModule.t.sol", "test": "testInstall", "name": "install unique entity module", - "gasUsed": 678930 + "gasUsed": 679000 }, { "file": "test/UniqueEntityModule.t.sol", @@ -279,7 +273,7 @@ "file": "test/UniqueEntityModule.t.sol", "test": "testInstallRoot", "name": "installRoot unique entity module", - "gasUsed": 668988 + "gasUsed": 669058 }, { "file": "test/UniqueEntityModule.t.sol", @@ -291,19 +285,19 @@ "file": "test/World.t.sol", "test": "testCall", "name": "call a system via the World", - "gasUsed": 12333 + "gasUsed": 12380 }, { "file": "test/World.t.sol", "test": "testCallFromUnlimitedDelegation", "name": "register an unlimited delegation", - "gasUsed": 50259 + "gasUsed": 50260 }, { "file": "test/World.t.sol", "test": "testCallFromUnlimitedDelegation", "name": "call a system via an unlimited delegation", - "gasUsed": 12682 + "gasUsed": 12729 }, { "file": "test/World.t.sol", @@ -321,37 +315,37 @@ "file": "test/World.t.sol", "test": "testRegisterFallbackSystem", "name": "Register a fallback system", - "gasUsed": 58888 + "gasUsed": 58889 }, { "file": "test/World.t.sol", "test": "testRegisterFallbackSystem", "name": "Register a root fallback system", - "gasUsed": 52205 + "gasUsed": 52206 }, { "file": "test/World.t.sol", "test": "testRegisterFunctionSelector", "name": "Register a function selector", - "gasUsed": 79482 + "gasUsed": 79483 }, { "file": "test/World.t.sol", "test": "testRegisterNamespace", "name": "Register a new namespace", - "gasUsed": 122752 + "gasUsed": 122753 }, { "file": "test/World.t.sol", "test": "testRegisterRootFunctionSelector", "name": "Register a root function selector", - "gasUsed": 74123 + "gasUsed": 74124 }, { "file": "test/World.t.sol", "test": "testRegisterTable", "name": "Register a new table in the namespace", - "gasUsed": 641673 + "gasUsed": 641683 }, { "file": "test/World.t.sol", diff --git a/packages/world/src/interfaces/IBaseWorld.sol b/packages/world/src/interfaces/IBaseWorld.sol index 92e5304434..1ef883047e 100644 --- a/packages/world/src/interfaces/IBaseWorld.sol +++ b/packages/world/src/interfaces/IBaseWorld.sol @@ -6,10 +6,10 @@ pragma solidity >=0.8.0; import { IStore } from "@latticexyz/store/src/IStore.sol"; import { IWorldKernel } from "../interfaces/IWorldKernel.sol"; -import { IBatchCallRootSystem } from "./IBatchCallRootSystem.sol"; import { ICoreSystem } from "./ICoreSystem.sol"; import { IAccessManagementSystem } from "./IAccessManagementSystem.sol"; import { IBalanceTransferSystem } from "./IBalanceTransferSystem.sol"; +import { ICallBatchSystem } from "./ICallBatchSystem.sol"; import { IModuleInstallationSystem } from "./IModuleInstallationSystem.sol"; import { IWorldRegistrationSystem } from "./IWorldRegistrationSystem.sol"; @@ -20,10 +20,10 @@ import { IWorldRegistrationSystem } from "./IWorldRegistrationSystem.sol"; interface IBaseWorld is IStore, IWorldKernel, - IBatchCallRootSystem, ICoreSystem, IAccessManagementSystem, IBalanceTransferSystem, + ICallBatchSystem, IModuleInstallationSystem, IWorldRegistrationSystem { diff --git a/packages/world/src/interfaces/IBatchCallSystem.sol b/packages/world/src/interfaces/IBatchCallSystem.sol deleted file mode 100644 index ec95752d02..0000000000 --- a/packages/world/src/interfaces/IBatchCallSystem.sol +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.8.0; - -interface IBatchCallSystem { - function batchCall_system_batchCall(bytes32[] memory resourceSelectors, bytes[] memory funcSelectorAndArgs) external; -} diff --git a/packages/world/src/interfaces/IBatchCallRootSystem.sol b/packages/world/src/interfaces/ICallBatchSystem.sol similarity index 58% rename from packages/world/src/interfaces/IBatchCallRootSystem.sol rename to packages/world/src/interfaces/ICallBatchSystem.sol index 92da60481d..62735ff5fa 100644 --- a/packages/world/src/interfaces/IBatchCallRootSystem.sol +++ b/packages/world/src/interfaces/ICallBatchSystem.sol @@ -3,6 +3,6 @@ pragma solidity >=0.8.0; /* Autogenerated file. Do not edit manually. */ -interface IBatchCallRootSystem { - function batchCall(bytes32[] memory resourceSelectors, bytes[] memory callDatas) external; +interface ICallBatchSystem { + function callBatch(bytes32[] memory resourceSelectors, bytes[] memory callDatas) external; } diff --git a/packages/world/src/modules/batchcallroot/BatchCallRootModule.sol b/packages/world/src/modules/batchcallroot/BatchCallRootModule.sol deleted file mode 100644 index ef61b38123..0000000000 --- a/packages/world/src/modules/batchcallroot/BatchCallRootModule.sol +++ /dev/null @@ -1,48 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.8.0; - -import { IBaseWorld } from "../../interfaces/IBaseWorld.sol"; - -import { Module } from "../../Module.sol"; -import { ResourceSelector } from "../../ResourceSelector.sol"; -import { revertWithBytes } from "../../revertWithBytes.sol"; -import { ROOT_NAMESPACE } from "../../constants.sol"; - -import { BatchCallRootSystem } from "./BatchCallRootSystem.sol"; -import { MODULE_NAME, SYSTEM_NAME } from "./constants.sol"; - -contract BatchCallRootModule is Module { - BatchCallRootSystem private immutable batchCallRootSystem = new BatchCallRootSystem(); - - function getName() public pure returns (bytes16) { - return MODULE_NAME; - } - - function installRoot(bytes memory) public { - IBaseWorld world = IBaseWorld(_world()); - - (bool success, bytes memory returnData) = address(world).delegatecall( - abi.encodeCall( - world.registerSystem, - (ResourceSelector.from(ROOT_NAMESPACE, SYSTEM_NAME), batchCallRootSystem, true) - ) - ); - if (!success) revertWithBytes(returnData); - - (success, returnData) = address(world).delegatecall( - abi.encodeCall( - world.registerRootFunctionSelector, - ( - ResourceSelector.from(ROOT_NAMESPACE, SYSTEM_NAME), - BatchCallRootSystem.batchCall.selector, - BatchCallRootSystem.batchCall.selector - ) - ) - ); - if (!success) revertWithBytes(returnData); - } - - function install(bytes memory) public pure { - revert NonRootInstallNotSupported(); - } -} diff --git a/packages/world/src/modules/batchcallroot/constants.sol b/packages/world/src/modules/batchcallroot/constants.sol deleted file mode 100644 index cd13ff66c0..0000000000 --- a/packages/world/src/modules/batchcallroot/constants.sol +++ /dev/null @@ -1,5 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.8.0; - -bytes16 constant MODULE_NAME = bytes16("batchCall.m"); -bytes16 constant SYSTEM_NAME = bytes16("system"); diff --git a/packages/world/src/modules/core/CoreModule.sol b/packages/world/src/modules/core/CoreModule.sol index 9e8698d72a..7a9729f71e 100644 --- a/packages/world/src/modules/core/CoreModule.sol +++ b/packages/world/src/modules/core/CoreModule.sol @@ -29,6 +29,7 @@ import { Balances } from "./tables/Balances.sol"; import { AccessManagementSystem } from "./implementations/AccessManagementSystem.sol"; import { BalanceTransferSystem } from "./implementations/BalanceTransferSystem.sol"; +import { CallBatchSystem } from "./implementations/CallBatchSystem.sol"; import { EphemeralRecordSystem } from "./implementations/EphemeralRecordSystem.sol"; import { ModuleInstallationSystem } from "./implementations/ModuleInstallationSystem.sol"; import { StoreRegistrationSystem } from "./implementations/StoreRegistrationSystem.sol"; @@ -101,7 +102,7 @@ contract CoreModule is Module { * Register function selectors for all CoreSystem functions in the World */ function _registerFunctionSelectors() internal { - bytes4[17] memory functionSelectors = [ + bytes4[18] memory functionSelectors = [ // --- AccessManagementSystem --- AccessManagementSystem.grantAccess.selector, AccessManagementSystem.revokeAccess.selector, @@ -109,6 +110,8 @@ contract CoreModule is Module { // --- BalanceTransferSystem --- BalanceTransferSystem.transferBalanceToNamespace.selector, BalanceTransferSystem.transferBalanceToAddress.selector, + // --- CallBatchSystem --- + CallBatchSystem.callBatch.selector, // --- EphemeralRecordSystem --- IStoreEphemeral.emitEphemeralRecord.selector, // --- ModuleInstallationSystem --- diff --git a/packages/world/src/modules/core/CoreSystem.sol b/packages/world/src/modules/core/CoreSystem.sol index 1a3b5c0c1c..35069bc60e 100644 --- a/packages/world/src/modules/core/CoreSystem.sol +++ b/packages/world/src/modules/core/CoreSystem.sol @@ -5,6 +5,7 @@ import { IWorldErrors } from "../../interfaces/IWorldErrors.sol"; import { AccessManagementSystem } from "./implementations/AccessManagementSystem.sol"; import { BalanceTransferSystem } from "./implementations/BalanceTransferSystem.sol"; +import { CallBatchSystem } from "./implementations/CallBatchSystem.sol"; import { EphemeralRecordSystem } from "./implementations/EphemeralRecordSystem.sol"; import { ModuleInstallationSystem } from "./implementations/ModuleInstallationSystem.sol"; import { StoreRegistrationSystem } from "./implementations/StoreRegistrationSystem.sol"; @@ -18,6 +19,7 @@ contract CoreSystem is IWorldErrors, AccessManagementSystem, BalanceTransferSystem, + CallBatchSystem, EphemeralRecordSystem, ModuleInstallationSystem, StoreRegistrationSystem, diff --git a/packages/world/src/modules/batchcallroot/BatchCallRootSystem.sol b/packages/world/src/modules/core/implementations/CallBatchSystem.sol similarity index 52% rename from packages/world/src/modules/batchcallroot/BatchCallRootSystem.sol rename to packages/world/src/modules/core/implementations/CallBatchSystem.sol index 7e42b21712..57f5f471c7 100644 --- a/packages/world/src/modules/batchcallroot/BatchCallRootSystem.sol +++ b/packages/world/src/modules/core/implementations/CallBatchSystem.sol @@ -1,11 +1,14 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; -import { System } from "../../System.sol"; -import { IBaseWorld } from "../../interfaces/IBaseWorld.sol"; -import { revertWithBytes } from "../../revertWithBytes.sol"; +import { System } from "../../../System.sol"; +import { IBaseWorld } from "../../../interfaces/IBaseWorld.sol"; +import { revertWithBytes } from "../../../revertWithBytes.sol"; -contract BatchCallRootSystem is System { - function batchCall(bytes32[] memory resourceSelectors, bytes[] memory callDatas) public { +/** + * Call multiple systems at the given resourceSelectors, on behalf of _msgSender(). + */ +contract CallBatchSystem is System { + function callBatch(bytes32[] memory resourceSelectors, bytes[] memory callDatas) public { IBaseWorld world = IBaseWorld(_world()); for (uint256 i; i < resourceSelectors.length; i++) { diff --git a/packages/world/test/BatchCallRootModule.t.sol b/packages/world/test/CallBatch.t.sol similarity index 77% rename from packages/world/test/BatchCallRootModule.t.sol rename to packages/world/test/CallBatch.t.sol index d12183f188..8e0e3d1424 100644 --- a/packages/world/test/BatchCallRootModule.t.sol +++ b/packages/world/test/CallBatch.t.sol @@ -8,13 +8,12 @@ import { World } from "../src/World.sol"; import { IBaseWorld } from "../src/interfaces/IBaseWorld.sol"; import { CoreModule } from "../src/modules/core/CoreModule.sol"; -import { BatchCallRootModule } from "../src/modules/batchcallroot/BatchCallRootModule.sol"; import { ResourceSelector } from "../src/ResourceSelector.sol"; import { WorldTestSystem } from "./World.t.sol"; -contract BatchCallRootModuleTest is Test, GasReporter { +contract CallBatchTest is Test, GasReporter { using ResourceSelector for bytes32; IBaseWorld world; @@ -24,11 +23,7 @@ contract BatchCallRootModuleTest is Test, GasReporter { world.initialize(new CoreModule()); } - function testInstallRoot() public { - startGasReport("installRoot batch call root module"); - world.installRootModule(new BatchCallRootModule(), new bytes(0)); - endGasReport(); - + function testCallBatch() public { // Register a new system WorldTestSystem system = new WorldTestSystem(); bytes32 resourceSelector = ResourceSelector.from("namespace", "testSystem"); @@ -44,7 +39,7 @@ contract BatchCallRootModuleTest is Test, GasReporter { callDatas[1] = abi.encodeWithSelector(WorldTestSystem.msgSender.selector); startGasReport("batch calling root"); - world.batchCall(resourceSelectors, callDatas); + world.callBatch(resourceSelectors, callDatas); endGasReport(); } } From 312dba9a58e238e3eadc4d05fab4ef2fa23b896c Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Mon, 18 Sep 2023 21:18:02 +0100 Subject: [PATCH 11/20] chore: rename gas report --- packages/world/gas-report.json | 2 +- packages/world/test/CallBatch.t.sol | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/world/gas-report.json b/packages/world/gas-report.json index 150cf4621a..d1e1e03c1e 100644 --- a/packages/world/gas-report.json +++ b/packages/world/gas-report.json @@ -38,7 +38,7 @@ { "file": "test/CallBatch.t.sol", "test": "testCallBatch", - "name": "batch calling root", + "name": "call systems with callBatch", "gasUsed": 45672 }, { diff --git a/packages/world/test/CallBatch.t.sol b/packages/world/test/CallBatch.t.sol index 8e0e3d1424..3f8a691212 100644 --- a/packages/world/test/CallBatch.t.sol +++ b/packages/world/test/CallBatch.t.sol @@ -38,7 +38,7 @@ contract CallBatchTest is Test, GasReporter { resourceSelectors[1] = resourceSelector; callDatas[1] = abi.encodeWithSelector(WorldTestSystem.msgSender.selector); - startGasReport("batch calling root"); + startGasReport("call systems with callBatch"); world.callBatch(resourceSelectors, callDatas); endGasReport(); } From f27d7a2248dc0d81853136691328d2a4de939abb Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Mon, 18 Sep 2023 22:31:36 +0100 Subject: [PATCH 12/20] feat: add return data to callBatch --- packages/world/gas-report.json | 36 +++++++++---------- .../world/src/interfaces/ICallBatchSystem.sol | 5 ++- .../core/implementations/CallBatchSystem.sol | 8 ++++- packages/world/test/CallBatch.t.sol | 21 +++++++---- 4 files changed, 44 insertions(+), 26 deletions(-) diff --git a/packages/world/gas-report.json b/packages/world/gas-report.json index d1e1e03c1e..3936f09a7e 100644 --- a/packages/world/gas-report.json +++ b/packages/world/gas-report.json @@ -39,19 +39,19 @@ "file": "test/CallBatch.t.sol", "test": "testCallBatch", "name": "call systems with callBatch", - "gasUsed": 45672 + "gasUsed": 46738 }, { "file": "test/KeysInTableModule.t.sol", "test": "testInstallComposite", "name": "install keys in table module", - "gasUsed": 1415164 + "gasUsed": 1415514 }, { "file": "test/KeysInTableModule.t.sol", "test": "testInstallGas", "name": "install keys in table module", - "gasUsed": 1415164 + "gasUsed": 1415514 }, { "file": "test/KeysInTableModule.t.sol", @@ -63,13 +63,13 @@ "file": "test/KeysInTableModule.t.sol", "test": "testInstallSingleton", "name": "install keys in table module", - "gasUsed": 1415164 + "gasUsed": 1415514 }, { "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookCompositeGas", "name": "install keys in table module", - "gasUsed": 1415164 + "gasUsed": 1415514 }, { "file": "test/KeysInTableModule.t.sol", @@ -87,7 +87,7 @@ "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookGas", "name": "install keys in table module", - "gasUsed": 1415164 + "gasUsed": 1415514 }, { "file": "test/KeysInTableModule.t.sol", @@ -105,7 +105,7 @@ "file": "test/KeysWithValueModule.t.sol", "test": "testGetKeysWithValueGas", "name": "install keys with value module", - "gasUsed": 654315 + "gasUsed": 654385 }, { "file": "test/KeysWithValueModule.t.sol", @@ -123,7 +123,7 @@ "file": "test/KeysWithValueModule.t.sol", "test": "testInstall", "name": "install keys with value module", - "gasUsed": 654315 + "gasUsed": 654385 }, { "file": "test/KeysWithValueModule.t.sol", @@ -135,7 +135,7 @@ "file": "test/KeysWithValueModule.t.sol", "test": "testSetAndDeleteRecordHook", "name": "install keys with value module", - "gasUsed": 654315 + "gasUsed": 654385 }, { "file": "test/KeysWithValueModule.t.sol", @@ -153,7 +153,7 @@ "file": "test/KeysWithValueModule.t.sol", "test": "testSetField", "name": "install keys with value module", - "gasUsed": 654315 + "gasUsed": 654385 }, { "file": "test/KeysWithValueModule.t.sol", @@ -237,7 +237,7 @@ "file": "test/StandardDelegationsModule.t.sol", "test": "testCallFromCallboundDelegation", "name": "register a callbound delegation", - "gasUsed": 113953 + "gasUsed": 113985 }, { "file": "test/StandardDelegationsModule.t.sol", @@ -249,7 +249,7 @@ "file": "test/StandardDelegationsModule.t.sol", "test": "testCallFromTimeboundDelegation", "name": "register a timebound delegation", - "gasUsed": 108390 + "gasUsed": 108422 }, { "file": "test/StandardDelegationsModule.t.sol", @@ -261,7 +261,7 @@ "file": "test/UniqueEntityModule.t.sol", "test": "testInstall", "name": "install unique entity module", - "gasUsed": 679000 + "gasUsed": 679131 }, { "file": "test/UniqueEntityModule.t.sol", @@ -273,7 +273,7 @@ "file": "test/UniqueEntityModule.t.sol", "test": "testInstallRoot", "name": "installRoot unique entity module", - "gasUsed": 669058 + "gasUsed": 669157 }, { "file": "test/UniqueEntityModule.t.sol", @@ -291,7 +291,7 @@ "file": "test/World.t.sol", "test": "testCallFromUnlimitedDelegation", "name": "register an unlimited delegation", - "gasUsed": 50260 + "gasUsed": 50292 }, { "file": "test/World.t.sol", @@ -315,7 +315,7 @@ "file": "test/World.t.sol", "test": "testRegisterFallbackSystem", "name": "Register a fallback system", - "gasUsed": 58889 + "gasUsed": 58953 }, { "file": "test/World.t.sol", @@ -327,7 +327,7 @@ "file": "test/World.t.sol", "test": "testRegisterFunctionSelector", "name": "Register a function selector", - "gasUsed": 79483 + "gasUsed": 79547 }, { "file": "test/World.t.sol", @@ -345,7 +345,7 @@ "file": "test/World.t.sol", "test": "testRegisterTable", "name": "Register a new table in the namespace", - "gasUsed": 641683 + "gasUsed": 641823 }, { "file": "test/World.t.sol", diff --git a/packages/world/src/interfaces/ICallBatchSystem.sol b/packages/world/src/interfaces/ICallBatchSystem.sol index 62735ff5fa..e7bf29af89 100644 --- a/packages/world/src/interfaces/ICallBatchSystem.sol +++ b/packages/world/src/interfaces/ICallBatchSystem.sol @@ -4,5 +4,8 @@ pragma solidity >=0.8.0; /* Autogenerated file. Do not edit manually. */ interface ICallBatchSystem { - function callBatch(bytes32[] memory resourceSelectors, bytes[] memory callDatas) external; + function callBatch( + bytes32[] memory resourceSelectors, + bytes[] memory callDatas + ) external returns (bytes[] memory datas); } diff --git a/packages/world/src/modules/core/implementations/CallBatchSystem.sol b/packages/world/src/modules/core/implementations/CallBatchSystem.sol index 57f5f471c7..6c2bed03f6 100644 --- a/packages/world/src/modules/core/implementations/CallBatchSystem.sol +++ b/packages/world/src/modules/core/implementations/CallBatchSystem.sol @@ -8,14 +8,20 @@ import { revertWithBytes } from "../../../revertWithBytes.sol"; * Call multiple systems at the given resourceSelectors, on behalf of _msgSender(). */ contract CallBatchSystem is System { - function callBatch(bytes32[] memory resourceSelectors, bytes[] memory callDatas) public { + function callBatch( + bytes32[] memory resourceSelectors, + bytes[] memory callDatas + ) public returns (bytes[] memory datas) { IBaseWorld world = IBaseWorld(_world()); + datas = new bytes[](resourceSelectors.length); for (uint256 i; i < resourceSelectors.length; i++) { (bool success, bytes memory returnData) = address(world).delegatecall( abi.encodeCall(world.call, (resourceSelectors[i], callDatas[i])) ); if (!success) revertWithBytes(returnData); + + datas[i] = abi.decode(returnData, (bytes)); } } } diff --git a/packages/world/test/CallBatch.t.sol b/packages/world/test/CallBatch.t.sol index 3f8a691212..a08dfd9bcb 100644 --- a/packages/world/test/CallBatch.t.sol +++ b/packages/world/test/CallBatch.t.sol @@ -13,6 +13,8 @@ import { ResourceSelector } from "../src/ResourceSelector.sol"; import { WorldTestSystem } from "./World.t.sol"; +address constant caller = address(0x01); + contract CallBatchTest is Test, GasReporter { using ResourceSelector for bytes32; @@ -24,22 +26,29 @@ contract CallBatchTest is Test, GasReporter { } function testCallBatch() public { + bytes16 namespace = "namespace"; + bytes16 name = "testSystem"; + bytes32 resourceSelector = ResourceSelector.from(namespace, name); + // Register a new system WorldTestSystem system = new WorldTestSystem(); - bytes32 resourceSelector = ResourceSelector.from("namespace", "testSystem"); - - world.registerSystem(resourceSelector, system, false); + world.registerSystem(resourceSelector, system, true); + // Batch call functions on the system bytes32[] memory resourceSelectors = new bytes32[](2); bytes[] memory callDatas = new bytes[](2); resourceSelectors[0] = resourceSelector; - callDatas[0] = abi.encodeWithSelector(WorldTestSystem.getStoreAddress.selector); + callDatas[0] = abi.encodeWithSelector(WorldTestSystem.msgSender.selector); resourceSelectors[1] = resourceSelector; - callDatas[1] = abi.encodeWithSelector(WorldTestSystem.msgSender.selector); + callDatas[1] = abi.encodeWithSelector(WorldTestSystem.getStoreAddress.selector); + vm.prank(caller); startGasReport("call systems with callBatch"); - world.callBatch(resourceSelectors, callDatas); + bytes[] memory datas = world.callBatch(resourceSelectors, callDatas); endGasReport(); + + assertEq(abi.decode(datas[0], (address)), caller, "wrong address returned"); + assertEq(abi.decode(datas[1], (address)), address(world), "wrong store returned"); } } From 2218493934bc5b8a4e510af6ed1813a7d2cbd0d4 Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Mon, 18 Sep 2023 22:37:40 +0100 Subject: [PATCH 13/20] chore: move comment --- .../src/modules/core/implementations/CallBatchSystem.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/world/src/modules/core/implementations/CallBatchSystem.sol b/packages/world/src/modules/core/implementations/CallBatchSystem.sol index 6c2bed03f6..9ed3138cf6 100644 --- a/packages/world/src/modules/core/implementations/CallBatchSystem.sol +++ b/packages/world/src/modules/core/implementations/CallBatchSystem.sol @@ -4,10 +4,10 @@ import { System } from "../../../System.sol"; import { IBaseWorld } from "../../../interfaces/IBaseWorld.sol"; import { revertWithBytes } from "../../../revertWithBytes.sol"; -/** - * Call multiple systems at the given resourceSelectors, on behalf of _msgSender(). - */ contract CallBatchSystem is System { + /** + * Call multiple systems at the given resourceSelectors, on behalf of _msgSender(). + */ function callBatch( bytes32[] memory resourceSelectors, bytes[] memory callDatas From 059b0e62dc14e7e290622f38a6a62695ba6f37e2 Mon Sep 17 00:00:00 2001 From: yonada Date: Tue, 19 Sep 2023 12:25:22 +0100 Subject: [PATCH 14/20] chore: modify comment Co-authored-by: alvarius --- .../world/src/modules/core/implementations/CallBatchSystem.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/world/src/modules/core/implementations/CallBatchSystem.sol b/packages/world/src/modules/core/implementations/CallBatchSystem.sol index 9ed3138cf6..14208d860b 100644 --- a/packages/world/src/modules/core/implementations/CallBatchSystem.sol +++ b/packages/world/src/modules/core/implementations/CallBatchSystem.sol @@ -6,7 +6,7 @@ import { revertWithBytes } from "../../../revertWithBytes.sol"; contract CallBatchSystem is System { /** - * Call multiple systems at the given resourceSelectors, on behalf of _msgSender(). + * Call multiple systems at the given resourceSelectors and return the array of return data. */ function callBatch( bytes32[] memory resourceSelectors, From 5cf3012077d137d20d9f41cd7e08cdd038943046 Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Tue, 19 Sep 2023 12:54:09 +0100 Subject: [PATCH 15/20] feat: use struct for callBatch arguments --- packages/world/src/interfaces/ICallBatchSystem.sol | 7 +++---- .../core/implementations/CallBatchSystem.sol | 14 ++++++-------- .../modules/core/implementations/SystemCall.sol | 7 +++++++ packages/world/test/CallBatch.t.sol | 12 +++++------- 4 files changed, 21 insertions(+), 19 deletions(-) create mode 100644 packages/world/src/modules/core/implementations/SystemCall.sol diff --git a/packages/world/src/interfaces/ICallBatchSystem.sol b/packages/world/src/interfaces/ICallBatchSystem.sol index e7bf29af89..525d648a35 100644 --- a/packages/world/src/interfaces/ICallBatchSystem.sol +++ b/packages/world/src/interfaces/ICallBatchSystem.sol @@ -3,9 +3,8 @@ pragma solidity >=0.8.0; /* Autogenerated file. Do not edit manually. */ +import { SystemCall } from "./../modules/core/implementations/SystemCall.sol"; + interface ICallBatchSystem { - function callBatch( - bytes32[] memory resourceSelectors, - bytes[] memory callDatas - ) external returns (bytes[] memory datas); + function callBatch(SystemCall[] memory systemCalls) external returns (bytes[] memory datas); } diff --git a/packages/world/src/modules/core/implementations/CallBatchSystem.sol b/packages/world/src/modules/core/implementations/CallBatchSystem.sol index 14208d860b..56e7b8df83 100644 --- a/packages/world/src/modules/core/implementations/CallBatchSystem.sol +++ b/packages/world/src/modules/core/implementations/CallBatchSystem.sol @@ -3,21 +3,19 @@ pragma solidity >=0.8.0; import { System } from "../../../System.sol"; import { IBaseWorld } from "../../../interfaces/IBaseWorld.sol"; import { revertWithBytes } from "../../../revertWithBytes.sol"; +import { SystemCall } from "./SystemCall.sol"; contract CallBatchSystem is System { /** - * Call multiple systems at the given resourceSelectors and return the array of return data. + * Call multiple systems at the given resource selectors and return the array of return data. */ - function callBatch( - bytes32[] memory resourceSelectors, - bytes[] memory callDatas - ) public returns (bytes[] memory datas) { + function callBatch(SystemCall[] memory systemCalls) public returns (bytes[] memory datas) { IBaseWorld world = IBaseWorld(_world()); - datas = new bytes[](resourceSelectors.length); + datas = new bytes[](systemCalls.length); - for (uint256 i; i < resourceSelectors.length; i++) { + for (uint256 i; i < systemCalls.length; i++) { (bool success, bytes memory returnData) = address(world).delegatecall( - abi.encodeCall(world.call, (resourceSelectors[i], callDatas[i])) + abi.encodeCall(world.call, (systemCalls[i].systemId, systemCalls[i].callData)) ); if (!success) revertWithBytes(returnData); diff --git a/packages/world/src/modules/core/implementations/SystemCall.sol b/packages/world/src/modules/core/implementations/SystemCall.sol new file mode 100644 index 0000000000..849b36d7a4 --- /dev/null +++ b/packages/world/src/modules/core/implementations/SystemCall.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +struct SystemCall { + bytes32 systemId; + bytes callData; +} diff --git a/packages/world/test/CallBatch.t.sol b/packages/world/test/CallBatch.t.sol index a08dfd9bcb..6cc8850051 100644 --- a/packages/world/test/CallBatch.t.sol +++ b/packages/world/test/CallBatch.t.sol @@ -8,6 +8,7 @@ import { World } from "../src/World.sol"; import { IBaseWorld } from "../src/interfaces/IBaseWorld.sol"; import { CoreModule } from "../src/modules/core/CoreModule.sol"; +import { SystemCall } from "../src/modules/core/implementations/SystemCall.sol"; import { ResourceSelector } from "../src/ResourceSelector.sol"; @@ -35,17 +36,14 @@ contract CallBatchTest is Test, GasReporter { world.registerSystem(resourceSelector, system, true); // Batch call functions on the system - bytes32[] memory resourceSelectors = new bytes32[](2); - bytes[] memory callDatas = new bytes[](2); + SystemCall[] memory systemCalls = new SystemCall[](2); - resourceSelectors[0] = resourceSelector; - callDatas[0] = abi.encodeWithSelector(WorldTestSystem.msgSender.selector); - resourceSelectors[1] = resourceSelector; - callDatas[1] = abi.encodeWithSelector(WorldTestSystem.getStoreAddress.selector); + systemCalls[0] = SystemCall(resourceSelector, abi.encodeWithSelector(WorldTestSystem.msgSender.selector)); + systemCalls[1] = SystemCall(resourceSelector, abi.encodeWithSelector(WorldTestSystem.getStoreAddress.selector)); vm.prank(caller); startGasReport("call systems with callBatch"); - bytes[] memory datas = world.callBatch(resourceSelectors, callDatas); + bytes[] memory datas = world.callBatch(systemCalls); endGasReport(); assertEq(abi.decode(datas[0], (address)), caller, "wrong address returned"); From 8300aa060d663646e72f0a9de238ddad60876a02 Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Tue, 19 Sep 2023 13:32:18 +0100 Subject: [PATCH 16/20] test: add access control tests for callBatch --- packages/world/test/CallBatch.t.sol | 74 +++++++++++++++++++++++++++-- 1 file changed, 69 insertions(+), 5 deletions(-) diff --git a/packages/world/test/CallBatch.t.sol b/packages/world/test/CallBatch.t.sol index 6cc8850051..eddbd39822 100644 --- a/packages/world/test/CallBatch.t.sol +++ b/packages/world/test/CallBatch.t.sol @@ -4,7 +4,10 @@ pragma solidity >=0.8.0; import { Test } from "forge-std/Test.sol"; import { GasReporter } from "@latticexyz/gas-report/src/GasReporter.sol"; +import { StoreSwitch } from "@latticexyz/store/src/StoreSwitch.sol"; + import { World } from "../src/World.sol"; +import { System } from "../src/System.sol"; import { IBaseWorld } from "../src/interfaces/IBaseWorld.sol"; import { CoreModule } from "../src/modules/core/CoreModule.sol"; @@ -12,9 +15,30 @@ import { SystemCall } from "../src/modules/core/implementations/SystemCall.sol"; import { ResourceSelector } from "../src/ResourceSelector.sol"; -import { WorldTestSystem } from "./World.t.sol"; +address constant caller = address(1); + +contract TestSystem is System { + address public admin; + uint256 public counter; + + function getStoreAddress() public view returns (address) { + return StoreSwitch.getStoreAddress(); + } + + function msgSender() public view returns (address) { + return _msgSender(); + } + + function setAdmin(address newAdmin) public { + admin = newAdmin; + } -address constant caller = address(0x01); + function increment() public { + require(_msgSender() == admin, "sender is incorrect"); + + counter++; + } +} contract CallBatchTest is Test, GasReporter { using ResourceSelector for bytes32; @@ -32,14 +56,54 @@ contract CallBatchTest is Test, GasReporter { bytes32 resourceSelector = ResourceSelector.from(namespace, name); // Register a new system - WorldTestSystem system = new WorldTestSystem(); + TestSystem system = new TestSystem(); + world.registerSystem(resourceSelector, system, true); + + // Try to increment the counter without setting the admin + SystemCall[] memory systemCalls = new SystemCall[](1); + systemCalls[0] = SystemCall(resourceSelector, abi.encodeWithSelector(TestSystem.increment.selector)); + + vm.expectRevert("sender is incorrect"); + world.callBatch(systemCalls); + + // Set the admin and increment the counter twice + systemCalls = new SystemCall[](3); + systemCalls[0] = SystemCall(resourceSelector, abi.encodeWithSelector(TestSystem.setAdmin.selector, caller)); + systemCalls[1] = SystemCall(resourceSelector, abi.encodeWithSelector(TestSystem.increment.selector)); + systemCalls[2] = SystemCall(resourceSelector, abi.encodeWithSelector(TestSystem.increment.selector)); + + vm.expectRevert("sender is incorrect"); + world.callBatch(systemCalls); + + vm.prank(caller); + world.callBatch(systemCalls); + + assertEq(system.counter(), 2, "wrong counter value"); + + // Increment the counter again + systemCalls = new SystemCall[](1); + systemCalls[0] = SystemCall(resourceSelector, abi.encodeWithSelector(TestSystem.increment.selector)); + + vm.prank(caller); + world.callBatch(systemCalls); + + assertEq(system.counter(), 3, "wrong counter value"); + } + + function testCallBatchReturnData() public { + bytes16 namespace = "namespace"; + bytes16 name = "testSystem"; + bytes32 resourceSelector = ResourceSelector.from(namespace, name); + + // Register a new system + TestSystem system = new TestSystem(); world.registerSystem(resourceSelector, system, true); // Batch call functions on the system SystemCall[] memory systemCalls = new SystemCall[](2); - systemCalls[0] = SystemCall(resourceSelector, abi.encodeWithSelector(WorldTestSystem.msgSender.selector)); - systemCalls[1] = SystemCall(resourceSelector, abi.encodeWithSelector(WorldTestSystem.getStoreAddress.selector)); + systemCalls[0] = SystemCall(resourceSelector, abi.encodeWithSelector(TestSystem.msgSender.selector)); + systemCalls[1] = SystemCall(resourceSelector, abi.encodeWithSelector(TestSystem.getStoreAddress.selector)); vm.prank(caller); startGasReport("call systems with callBatch"); From 480b721ec23ad50c2cff4fa935c15a193c50efa4 Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Tue, 19 Sep 2023 13:32:43 +0100 Subject: [PATCH 17/20] chore: tweak comment --- packages/world/test/CallBatch.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/world/test/CallBatch.t.sol b/packages/world/test/CallBatch.t.sol index eddbd39822..e9e859ad1d 100644 --- a/packages/world/test/CallBatch.t.sol +++ b/packages/world/test/CallBatch.t.sol @@ -34,7 +34,7 @@ contract TestSystem is System { } function increment() public { - require(_msgSender() == admin, "sender is incorrect"); + require(_msgSender() == admin, "sender is not admin"); counter++; } From 1affa390f44f49f8e3536234adc41e7014fef02f Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Tue, 19 Sep 2023 13:35:09 +0100 Subject: [PATCH 18/20] refactor: move test constants --- packages/world/test/CallBatch.t.sol | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/packages/world/test/CallBatch.t.sol b/packages/world/test/CallBatch.t.sol index e9e859ad1d..f3e66413a5 100644 --- a/packages/world/test/CallBatch.t.sol +++ b/packages/world/test/CallBatch.t.sol @@ -44,6 +44,9 @@ contract CallBatchTest is Test, GasReporter { using ResourceSelector for bytes32; IBaseWorld world; + bytes16 namespace = "namespace"; + bytes16 name = "testSystem"; + bytes32 resourceSelector = ResourceSelector.from(namespace, name); function setUp() public { world = IBaseWorld(address(new World())); @@ -51,10 +54,6 @@ contract CallBatchTest is Test, GasReporter { } function testCallBatch() public { - bytes16 namespace = "namespace"; - bytes16 name = "testSystem"; - bytes32 resourceSelector = ResourceSelector.from(namespace, name); - // Register a new system TestSystem system = new TestSystem(); world.registerSystem(resourceSelector, system, true); @@ -63,7 +62,7 @@ contract CallBatchTest is Test, GasReporter { SystemCall[] memory systemCalls = new SystemCall[](1); systemCalls[0] = SystemCall(resourceSelector, abi.encodeWithSelector(TestSystem.increment.selector)); - vm.expectRevert("sender is incorrect"); + vm.expectRevert("sender is not admin"); world.callBatch(systemCalls); // Set the admin and increment the counter twice @@ -72,7 +71,7 @@ contract CallBatchTest is Test, GasReporter { systemCalls[1] = SystemCall(resourceSelector, abi.encodeWithSelector(TestSystem.increment.selector)); systemCalls[2] = SystemCall(resourceSelector, abi.encodeWithSelector(TestSystem.increment.selector)); - vm.expectRevert("sender is incorrect"); + vm.expectRevert("sender is not admin"); world.callBatch(systemCalls); vm.prank(caller); @@ -91,10 +90,6 @@ contract CallBatchTest is Test, GasReporter { } function testCallBatchReturnData() public { - bytes16 namespace = "namespace"; - bytes16 name = "testSystem"; - bytes32 resourceSelector = ResourceSelector.from(namespace, name); - // Register a new system TestSystem system = new TestSystem(); world.registerSystem(resourceSelector, system, true); From 9f8a2a2820b146d9e9e51e30b887e9f31b84e16a Mon Sep 17 00:00:00 2001 From: alvrs Date: Thu, 21 Sep 2023 20:09:58 +0100 Subject: [PATCH 19/20] gas report --- packages/world/gas-report.json | 112 +++++++-------------------------- 1 file changed, 22 insertions(+), 90 deletions(-) diff --git a/packages/world/gas-report.json b/packages/world/gas-report.json index f02a6c1cef..621b6463d0 100644 --- a/packages/world/gas-report.json +++ b/packages/world/gas-report.json @@ -39,27 +39,19 @@ "file": "test/CallBatch.t.sol", "test": "testCallBatchReturnData", "name": "call systems with callBatch", - "gasUsed": 46388 + "gasUsed": 45546 }, { "file": "test/KeysInTableModule.t.sol", "test": "testInstallComposite", "name": "install keys in table module", -<<<<<<< HEAD - "gasUsed": 1414002 -======= - "gasUsed": 1414653 ->>>>>>> main + "gasUsed": 1415069 }, { "file": "test/KeysInTableModule.t.sol", "test": "testInstallGas", "name": "install keys in table module", -<<<<<<< HEAD - "gasUsed": 1414002 -======= - "gasUsed": 1414653 ->>>>>>> main + "gasUsed": 1415069 }, { "file": "test/KeysInTableModule.t.sol", @@ -71,21 +63,13 @@ "file": "test/KeysInTableModule.t.sol", "test": "testInstallSingleton", "name": "install keys in table module", -<<<<<<< HEAD - "gasUsed": 1414002 -======= - "gasUsed": 1414653 ->>>>>>> main + "gasUsed": 1415069 }, { "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookCompositeGas", "name": "install keys in table module", -<<<<<<< HEAD - "gasUsed": 1414002 -======= - "gasUsed": 1414653 ->>>>>>> main + "gasUsed": 1415069 }, { "file": "test/KeysInTableModule.t.sol", @@ -103,11 +87,7 @@ "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookGas", "name": "install keys in table module", -<<<<<<< HEAD - "gasUsed": 1414002 -======= - "gasUsed": 1414653 ->>>>>>> main + "gasUsed": 1415069 }, { "file": "test/KeysInTableModule.t.sol", @@ -125,11 +105,7 @@ "file": "test/KeysWithValueModule.t.sol", "test": "testGetKeysWithValueGas", "name": "install keys with value module", -<<<<<<< HEAD - "gasUsed": 652923 -======= - "gasUsed": 665378 ->>>>>>> main + "gasUsed": 665448 }, { "file": "test/KeysWithValueModule.t.sol", @@ -147,11 +123,7 @@ "file": "test/KeysWithValueModule.t.sol", "test": "testInstall", "name": "install keys with value module", -<<<<<<< HEAD - "gasUsed": 652923 -======= - "gasUsed": 665378 ->>>>>>> main + "gasUsed": 665448 }, { "file": "test/KeysWithValueModule.t.sol", @@ -163,11 +135,7 @@ "file": "test/KeysWithValueModule.t.sol", "test": "testSetAndDeleteRecordHook", "name": "install keys with value module", -<<<<<<< HEAD - "gasUsed": 652923 -======= - "gasUsed": 665378 ->>>>>>> main + "gasUsed": 665448 }, { "file": "test/KeysWithValueModule.t.sol", @@ -185,11 +153,7 @@ "file": "test/KeysWithValueModule.t.sol", "test": "testSetField", "name": "install keys with value module", -<<<<<<< HEAD - "gasUsed": 652923 -======= - "gasUsed": 665378 ->>>>>>> main + "gasUsed": 665448 }, { "file": "test/KeysWithValueModule.t.sol", @@ -273,11 +237,7 @@ "file": "test/StandardDelegationsModule.t.sol", "test": "testCallFromCallboundDelegation", "name": "register a callbound delegation", -<<<<<<< HEAD - "gasUsed": 113942 -======= - "gasUsed": 114589 ->>>>>>> main + "gasUsed": 114729 }, { "file": "test/StandardDelegationsModule.t.sol", @@ -289,11 +249,7 @@ "file": "test/StandardDelegationsModule.t.sol", "test": "testCallFromTimeboundDelegation", "name": "register a timebound delegation", -<<<<<<< HEAD - "gasUsed": 108406 -======= - "gasUsed": 109084 ->>>>>>> main + "gasUsed": 109224 }, { "file": "test/StandardDelegationsModule.t.sol", @@ -305,11 +261,7 @@ "file": "test/UniqueEntityModule.t.sol", "test": "testInstall", "name": "install unique entity module", -<<<<<<< HEAD - "gasUsed": 679188 -======= - "gasUsed": 690431 ->>>>>>> main + "gasUsed": 690539 }, { "file": "test/UniqueEntityModule.t.sol", @@ -321,11 +273,7 @@ "file": "test/UniqueEntityModule.t.sol", "test": "testInstallRoot", "name": "installRoot unique entity module", -<<<<<<< HEAD - "gasUsed": 669241 -======= - "gasUsed": 680681 ->>>>>>> main + "gasUsed": 680735 }, { "file": "test/UniqueEntityModule.t.sol", @@ -343,11 +291,7 @@ "file": "test/World.t.sol", "test": "testCallFromUnlimitedDelegation", "name": "register an unlimited delegation", -<<<<<<< HEAD - "gasUsed": 50253 -======= - "gasUsed": 50629 ->>>>>>> main + "gasUsed": 50617 }, { "file": "test/World.t.sol", @@ -371,55 +315,43 @@ "file": "test/World.t.sol", "test": "testRegisterFallbackSystem", "name": "Register a fallback system", -<<<<<<< HEAD - "gasUsed": 58916 -======= - "gasUsed": 59306 ->>>>>>> main + "gasUsed": 59347 }, { "file": "test/World.t.sol", "test": "testRegisterFallbackSystem", "name": "Register a root fallback system", - "gasUsed": 52950 + "gasUsed": 52972 }, { "file": "test/World.t.sol", "test": "testRegisterFunctionSelector", "name": "Register a function selector", -<<<<<<< HEAD - "gasUsed": 79510 -======= - "gasUsed": 79872 ->>>>>>> main + "gasUsed": 79913 }, { "file": "test/World.t.sol", "test": "testRegisterNamespace", "name": "Register a new namespace", - "gasUsed": 123269 + "gasUsed": 123225 }, { "file": "test/World.t.sol", "test": "testRegisterRootFunctionSelector", "name": "Register a root function selector", - "gasUsed": 74863 + "gasUsed": 74885 }, { "file": "test/World.t.sol", "test": "testRegisterSystem", "name": "register a system", - "gasUsed": 165750 + "gasUsed": 165772 }, { "file": "test/World.t.sol", "test": "testRegisterTable", "name": "Register a new table in the namespace", -<<<<<<< HEAD - "gasUsed": 641906 -======= - "gasUsed": 651802 ->>>>>>> main + "gasUsed": 651898 }, { "file": "test/World.t.sol", From 4ce27d2b95d48366769cdb0ffd29fe228e5d1903 Mon Sep 17 00:00:00 2001 From: alvarius Date: Thu, 21 Sep 2023 20:14:29 +0100 Subject: [PATCH 20/20] Create wild-nails-wonder.md --- .changeset/wild-nails-wonder.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .changeset/wild-nails-wonder.md diff --git a/.changeset/wild-nails-wonder.md b/.changeset/wild-nails-wonder.md new file mode 100644 index 0000000000..3543079a4f --- /dev/null +++ b/.changeset/wild-nails-wonder.md @@ -0,0 +1,13 @@ +--- +"@latticexyz/world": minor +--- + +The `World` now has a `callBatch` method which allows multiple system calls to be batched into a single transaction. + +```solidity +import { SystemCallData } from "@latticexyz/world/modules/core/types.sol"; + +interface IBaseWorld { + function callBatch(SystemCallData[] calldata systemCalls) external returns (bytes[] memory returnDatas); +} +```