Skip to content

Commit

Permalink
feat(world): world kernel inherits IModuleErrors (#2380)
Browse files Browse the repository at this point in the history
  • Loading branch information
yonadaaa committed Mar 7, 2024
1 parent 86766ce commit be18b75
Show file tree
Hide file tree
Showing 9 changed files with 40 additions and 42 deletions.
5 changes: 5 additions & 0 deletions .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.
30 changes: 1 addition & 29 deletions docs/pages/world/reference/module-external.mdx
Expand Up @@ -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.
Expand Down Expand Up @@ -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);
```
5 changes: 4 additions & 1 deletion docs/pages/world/reference/world-external.mdx
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion packages/world-modules/test/KeysInTableModule.t.sol
Expand Up @@ -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";
Expand Down Expand Up @@ -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));
}

Expand Down
3 changes: 2 additions & 1 deletion packages/world-modules/test/KeysWithValueModule.t.sol
Expand Up @@ -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";
Expand Down Expand Up @@ -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));
}

Expand Down
5 changes: 3 additions & 2 deletions packages/world-modules/test/UniqueEntityModule.t.sol
Expand Up @@ -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";
Expand Down Expand Up @@ -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));
}

Expand All @@ -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));
}

Expand Down
9 changes: 2 additions & 7 deletions packages/world/src/IModule.sol
Expand Up @@ -2,6 +2,7 @@
pragma solidity >=0.8.24;

import { IERC165 } from "./IERC165.sol";
import { IModuleErrors } from "./IModuleErrors.sol";

/**
* @title IModule
Expand All @@ -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.
Expand Down
17 changes: 17 additions & 0 deletions 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);
}
5 changes: 4 additions & 1 deletion packages/world/src/IWorldKernel.sol
Expand Up @@ -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";

/**
Expand Down Expand Up @@ -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.
Expand Down

0 comments on commit be18b75

Please sign in to comment.