diff --git a/.changeset/strange-cheetahs-drop.md b/.changeset/strange-cheetahs-drop.md new file mode 100644 index 0000000000..dfe0fcc63d --- /dev/null +++ b/.changeset/strange-cheetahs-drop.md @@ -0,0 +1,5 @@ +--- +"@latticexyz/world": patch +--- + +`IWorldKernel` now inherits `IModuleErrors` so it can render the correct errors if the World reverts when delegatecalled with Module code. diff --git a/docs/pages/world/reference/module-external.mdx b/docs/pages/world/reference/module-external.mdx index 3e668cde6c..f7c8ff8a64 100644 --- a/docs/pages/world/reference/module-external.mdx +++ b/docs/pages/world/reference/module-external.mdx @@ -5,7 +5,7 @@ [Git Source](https://github.com/latticexyz/mud/blob/main/packages/world/src/IModule.sol) **Inherits:** -[IERC165](/world/reference/internal/erc165-external#ierc165) +[IERC165](/world/reference/internal/erc165-external#ierc165), [IModuleErrors](/src/IModuleErrors.sol/interface.IModuleErrors.md) _Interface for the Module system. A module can be installed within the context of a world, either as a root or non-root module. @@ -47,31 +47,3 @@ function install(bytes memory encodedArgs) external; | Name | Type | Description | | ------------- | ------- | ----------------------------------------------------------------------------- | | `encodedArgs` | `bytes` | The ABI encoded arguments that may be needed during the installation process. | - -### Errors - -#### Module_RootInstallNotSupported - -_Errors to represent non-support of specific installation types._ - -```solidity -error Module_RootInstallNotSupported(); -``` - -#### Module_NonRootInstallNotSupported - -```solidity -error Module_NonRootInstallNotSupported(); -``` - -#### Module_AlreadyInstalled - -```solidity -error Module_AlreadyInstalled(); -``` - -#### Module_MissingDependency - -```solidity -error Module_MissingDependency(address dependency); -``` diff --git a/docs/pages/world/reference/world-external.mdx b/docs/pages/world/reference/world-external.mdx index 9c7bbd883c..8cf549df69 100644 --- a/docs/pages/world/reference/world-external.mdx +++ b/docs/pages/world/reference/world-external.mdx @@ -591,12 +591,15 @@ function callFrom( [Git Source](https://github.com/latticexyz/mud/blob/main/packages/world/src/IWorldKernel.sol) **Inherits:** -[IWorldModuleInstallation](/world/reference/world-external#iworldmoduleinstallation), [IWorldCall](/world/reference/world-external#iworldcall), [IWorldErrors](/world/reference/world-external#iworlderrors), [IWorldEvents](/src/IWorldEvents.sol/interface.IWorldEvents.md) +[IWorldModuleInstallation](/world/reference/world-external#iworldmoduleinstallation), [IWorldCall](/world/reference/world-external#iworldcall), [IWorldErrors](/world/reference/world-external#iworlderrors), [IWorldEvents](/src/IWorldEvents.sol/interface.IWorldEvents.md), [IModuleErrors](/src/IModuleErrors.sol/interface.IModuleErrors.md) The IWorldKernel interface includes all methods that are part of the World contract's internal bytecode. Consumers should use the `IBaseWorld` interface instead, which includes dynamically registered functions selectors from the `InitModule`. +_The IWorldKernel interface inherits IModuleErrors because the world can be delegatecalled with module code, +so it's ABI should include these errors._ + ### Functions #### worldVersion diff --git a/packages/world-modules/test/KeysInTableModule.t.sol b/packages/world-modules/test/KeysInTableModule.t.sol index 58b5da867b..51c7da710c 100644 --- a/packages/world-modules/test/KeysInTableModule.t.sol +++ b/packages/world-modules/test/KeysInTableModule.t.sol @@ -13,6 +13,7 @@ import { SchemaType } from "@latticexyz/schema-type/src/solidity/SchemaType.sol" import { World } from "@latticexyz/world/src/World.sol"; import { IModule } from "@latticexyz/world/src/IModule.sol"; +import { IModuleErrors } from "@latticexyz/world/src/IModule.sol"; import { IBaseWorld } from "@latticexyz/world/src/codegen/interfaces/IBaseWorld.sol"; import { ResourceId, WorldResourceIdLib, WorldResourceIdInstance } from "@latticexyz/world/src/WorldResourceId.sol"; import { ROOT_NAMESPACE } from "@latticexyz/world/src/constants.sol"; @@ -163,7 +164,7 @@ contract KeysInTableModuleTest is Test, GasReporter { function testInstallTwice() public { world.installRootModule(keysInTableModule, abi.encode(tableId)); - vm.expectRevert(IModule.Module_AlreadyInstalled.selector); + vm.expectRevert(IModuleErrors.Module_AlreadyInstalled.selector); world.installRootModule(keysInTableModule, abi.encode(tableId)); } diff --git a/packages/world-modules/test/KeysWithValueModule.t.sol b/packages/world-modules/test/KeysWithValueModule.t.sol index 56ae3017e9..ce4a1ae38e 100644 --- a/packages/world-modules/test/KeysWithValueModule.t.sol +++ b/packages/world-modules/test/KeysWithValueModule.t.sol @@ -16,6 +16,7 @@ import { FieldLayoutEncodeHelper } from "@latticexyz/store/test/FieldLayoutEncod import { World } from "@latticexyz/world/src/World.sol"; import { IModule } from "@latticexyz/world/src/IModule.sol"; +import { IModuleErrors } from "@latticexyz/world/src/IModuleErrors.sol"; import { IBaseWorld } from "@latticexyz/world/src/codegen/interfaces/IBaseWorld.sol"; import { WorldResourceIdLib, WorldResourceIdInstance, NAME_BITS, TYPE_BITS } from "@latticexyz/world/src/WorldResourceId.sol"; import { ROOT_NAMESPACE } from "@latticexyz/world/src/constants.sol"; @@ -115,7 +116,7 @@ contract KeysWithValueModuleTest is Test, GasReporter { ); world.installRootModule(keysWithValueModule, abi.encode(sourceTableId)); - vm.expectRevert(IModule.Module_AlreadyInstalled.selector); + vm.expectRevert(IModuleErrors.Module_AlreadyInstalled.selector); world.installRootModule(keysWithValueModule, abi.encode(sourceTableId)); } diff --git a/packages/world-modules/test/UniqueEntityModule.t.sol b/packages/world-modules/test/UniqueEntityModule.t.sol index 744349280c..65925889a8 100644 --- a/packages/world-modules/test/UniqueEntityModule.t.sol +++ b/packages/world-modules/test/UniqueEntityModule.t.sol @@ -7,6 +7,7 @@ import { GasReporter } from "@latticexyz/gas-report/src/GasReporter.sol"; import { StoreSwitch } from "@latticexyz/store/src/StoreSwitch.sol"; import { World } from "@latticexyz/world/src/World.sol"; import { IModule } from "@latticexyz/world/src/IModule.sol"; +import { IModuleErrors } from "@latticexyz/world/src/IModuleErrors.sol"; import { IBaseWorld } from "@latticexyz/world/src/codegen/interfaces/IBaseWorld.sol"; import { IWorldErrors } from "@latticexyz/world/src/IWorldErrors.sol"; import { System } from "@latticexyz/world/src/System.sol"; @@ -60,7 +61,7 @@ contract UniqueEntityModuleTest is Test, GasReporter { function testInstallTwice() public { world.installModule(uniqueEntityModule, new bytes(0)); - vm.expectRevert(IModule.Module_AlreadyInstalled.selector); + vm.expectRevert(IModuleErrors.Module_AlreadyInstalled.selector); world.installModule(uniqueEntityModule, new bytes(0)); } @@ -84,7 +85,7 @@ contract UniqueEntityModuleTest is Test, GasReporter { function testInstallRootTwice() public { world.installRootModule(uniqueEntityModule, new bytes(0)); - vm.expectRevert(IModule.Module_AlreadyInstalled.selector); + vm.expectRevert(IModuleErrors.Module_AlreadyInstalled.selector); world.installRootModule(uniqueEntityModule, new bytes(0)); } diff --git a/packages/world/src/IModule.sol b/packages/world/src/IModule.sol index 25adb58993..2c2cb9ec21 100644 --- a/packages/world/src/IModule.sol +++ b/packages/world/src/IModule.sol @@ -2,6 +2,7 @@ pragma solidity >=0.8.24; import { IERC165 } from "./IERC165.sol"; +import { IModuleErrors } from "./IModuleErrors.sol"; /** * @title IModule @@ -10,13 +11,7 @@ import { IERC165 } from "./IERC165.sol"; * A module can be installed within the context of a world, either as a root or non-root module. * This interface adheres to the ERC-165 standard for determining interface support. */ -interface IModule is IERC165 { - /// @dev Errors to represent non-support of specific installation types. - error Module_RootInstallNotSupported(); - error Module_NonRootInstallNotSupported(); - error Module_AlreadyInstalled(); - error Module_MissingDependency(address dependency); - +interface IModule is IERC165, IModuleErrors { /** * @notice Installs the module as a root module. * @dev This function is invoked by the World contract during `installRootModule` process. diff --git a/packages/world/src/IModuleErrors.sol b/packages/world/src/IModuleErrors.sol new file mode 100644 index 0000000000..393049dd0c --- /dev/null +++ b/packages/world/src/IModuleErrors.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.24; + +/** + * @title IModuleErrors + * @author MUD (https://mud.dev) by Lattice (https://lattice.xyz) + * @notice This interface includes errors for the Module library. + * @dev We bundle these errors in an interface (instead of at the file-level or in their corresponding library) so they can be inherited by IWorldKernel. + * This ensures that all errors are included in the IWorldKernel ABI for proper decoding in the frontend. + */ +interface IModuleErrors { + /// @dev Errors to represent non-support of specific installation types. + error Module_RootInstallNotSupported(); + error Module_NonRootInstallNotSupported(); + error Module_AlreadyInstalled(); + error Module_MissingDependency(address dependency); +} diff --git a/packages/world/src/IWorldKernel.sol b/packages/world/src/IWorldKernel.sol index a7c13dde7b..63b64d7470 100644 --- a/packages/world/src/IWorldKernel.sol +++ b/packages/world/src/IWorldKernel.sol @@ -4,6 +4,7 @@ pragma solidity >=0.8.24; import { IWorldErrors } from "./IWorldErrors.sol"; import { IModule } from "./IModule.sol"; import { ResourceId } from "./WorldResourceId.sol"; +import { IModuleErrors } from "./IModuleErrors.sol"; import { IWorldEvents } from "./IWorldEvents.sol"; /** @@ -59,8 +60,10 @@ interface IWorldCall { * @notice The IWorldKernel interface includes all methods that are part of the World contract's * internal bytecode. Consumers should use the `IBaseWorld` interface instead, which includes dynamically * registered functions selectors from the `InitModule`. + * @dev The IWorldKernel interface inherits IModuleErrors because the world can be delegatecalled with module code, + * so it's ABI should include these errors. */ -interface IWorldKernel is IWorldModuleInstallation, IWorldCall, IWorldErrors, IWorldEvents { +interface IWorldKernel is IWorldModuleInstallation, IWorldCall, IWorldErrors, IWorldEvents, IModuleErrors { /** * @notice Retrieve the version of the World. * @return The version identifier of the World.