Skip to content

Commit

Permalink
feat(world): add isInstalled to Module (#2056)
Browse files Browse the repository at this point in the history
Co-authored-by: Kevin Ingersoll <kingersoll@gmail.com>
  • Loading branch information
R-Morpheus and holic committed Jan 2, 2024
1 parent de05c33 commit eb384bb
Show file tree
Hide file tree
Showing 8 changed files with 48 additions and 33 deletions.
6 changes: 6 additions & 0 deletions .changeset/grumpy-icons-sleep.md
@@ -0,0 +1,6 @@
---
"@latticexyz/world-modules": patch
"@latticexyz/world": patch
---

Added `isInstalled` and `requireNotInstalled` helpers to `Module` base contract.
22 changes: 11 additions & 11 deletions packages/world-modules/gas-report.json
Expand Up @@ -75,13 +75,13 @@
"file": "test/KeysInTableModule.t.sol",
"test": "testInstallComposite",
"name": "install keys in table module",
"gasUsed": 1409435
"gasUsed": 1409529
},
{
"file": "test/KeysInTableModule.t.sol",
"test": "testInstallGas",
"name": "install keys in table module",
"gasUsed": 1409435
"gasUsed": 1409529
},
{
"file": "test/KeysInTableModule.t.sol",
Expand All @@ -93,13 +93,13 @@
"file": "test/KeysInTableModule.t.sol",
"test": "testInstallSingleton",
"name": "install keys in table module",
"gasUsed": 1409435
"gasUsed": 1409529
},
{
"file": "test/KeysInTableModule.t.sol",
"test": "testSetAndDeleteRecordHookCompositeGas",
"name": "install keys in table module",
"gasUsed": 1409435
"gasUsed": 1409529
},
{
"file": "test/KeysInTableModule.t.sol",
Expand All @@ -117,7 +117,7 @@
"file": "test/KeysInTableModule.t.sol",
"test": "testSetAndDeleteRecordHookGas",
"name": "install keys in table module",
"gasUsed": 1409435
"gasUsed": 1409529
},
{
"file": "test/KeysInTableModule.t.sol",
Expand All @@ -135,7 +135,7 @@
"file": "test/KeysWithValueModule.t.sol",
"test": "testGetKeysWithValueGas",
"name": "install keys with value module",
"gasUsed": 650708
"gasUsed": 650796
},
{
"file": "test/KeysWithValueModule.t.sol",
Expand All @@ -153,7 +153,7 @@
"file": "test/KeysWithValueModule.t.sol",
"test": "testInstall",
"name": "install keys with value module",
"gasUsed": 650708
"gasUsed": 650796
},
{
"file": "test/KeysWithValueModule.t.sol",
Expand All @@ -165,7 +165,7 @@
"file": "test/KeysWithValueModule.t.sol",
"test": "testSetAndDeleteRecordHook",
"name": "install keys with value module",
"gasUsed": 650708
"gasUsed": 650796
},
{
"file": "test/KeysWithValueModule.t.sol",
Expand All @@ -183,7 +183,7 @@
"file": "test/KeysWithValueModule.t.sol",
"test": "testSetField",
"name": "install keys with value module",
"gasUsed": 650708
"gasUsed": 650796
},
{
"file": "test/KeysWithValueModule.t.sol",
Expand Down Expand Up @@ -303,7 +303,7 @@
"file": "test/UniqueEntityModule.t.sol",
"test": "testInstall",
"name": "install unique entity module",
"gasUsed": 678996
"gasUsed": 679102
},
{
"file": "test/UniqueEntityModule.t.sol",
Expand All @@ -315,7 +315,7 @@
"file": "test/UniqueEntityModule.t.sol",
"test": "testInstallRoot",
"name": "installRoot unique entity module",
"gasUsed": 644433
"gasUsed": 644539
},
{
"file": "test/UniqueEntityModule.t.sol",
Expand Down
Expand Up @@ -6,7 +6,6 @@ import { ResourceId } from "@latticexyz/store/src/ResourceId.sol";
import { Module } from "@latticexyz/world/src/Module.sol";
import { WorldResourceIdLib } from "@latticexyz/world/src/WorldResourceId.sol";
import { IBaseWorld } from "@latticexyz/world/src/codegen/interfaces/IBaseWorld.sol";
import { InstalledModules } from "@latticexyz/world/src/codegen/tables/InstalledModules.sol";

import { Puppet } from "../puppet/Puppet.sol";
import { createPuppet } from "../puppet/createPuppet.sol";
Expand All @@ -32,16 +31,14 @@ contract ERC20Module is Module {

function _requireDependencies() internal view {
// Require PuppetModule to be installed
if (InstalledModules.get(PUPPET_MODULE_NAME, keccak256(new bytes(0))) == address(0)) {
if (!isInstalled(PUPPET_MODULE_NAME, new bytes(0))) {
revert Module_MissingDependency(string(bytes.concat(PUPPET_MODULE_NAME)));
}
}

function install(bytes memory args) public {
// Require the module to not be installed with these args yet
if (InstalledModules.get(MODULE_NAME, keccak256(args)) != address(0)) {
revert Module_AlreadyInstalled();
}
requireNotInstalled(MODULE_NAME, args);

// Extract args
(bytes14 namespace, ERC20MetadataData memory metadata) = abi.decode(args, (bytes14, ERC20MetadataData));
Expand Down
Expand Up @@ -33,18 +33,16 @@ contract ERC721Module is Module {
return MODULE_NAME;
}

function _requireDependencies() internal {
function _requireDependencies() internal view {
// Require PuppetModule to be installed
if (InstalledModules.get(PUPPET_MODULE_NAME, keccak256(new bytes(0))) == address(0)) {
if (!isInstalled(PUPPET_MODULE_NAME, new bytes(0))) {
revert Module_MissingDependency(string(bytes.concat(PUPPET_MODULE_NAME)));
}
}

function install(bytes memory args) public {
// Require the module to not be installed with these args yet
if (InstalledModules.get(MODULE_NAME, keccak256(args)) != address(0)) {
revert Module_AlreadyInstalled();
}
requireNotInstalled(MODULE_NAME, args);

// Extract args
(bytes14 namespace, ERC721MetadataData memory metadata) = abi.decode(args, (bytes14, ERC721MetadataData));
Expand Down
Expand Up @@ -40,9 +40,7 @@ contract KeysInTableModule is Module {
function installRoot(bytes memory args) public override {
// Naive check to ensure this is only installed once
// TODO: only revert if there's nothing to do
if (InstalledModules.getModuleAddress(getName(), keccak256(args)) != address(0)) {
revert Module_AlreadyInstalled();
}
requireNotInstalled(getName(), args);

// Extract source table id from args
ResourceId sourceTableId = ResourceId.wrap(abi.decode(args, (bytes32)));
Expand Down
Expand Up @@ -41,9 +41,7 @@ contract KeysWithValueModule is Module {
function installRoot(bytes memory args) public {
// Naive check to ensure this is only installed once
// TODO: only revert if there's nothing to do
if (InstalledModules.getModuleAddress(getName(), keccak256(args)) != address(0)) {
revert Module_AlreadyInstalled();
}
requireNotInstalled(getName(), args);

// Extract source table id from args
ResourceId sourceTableId = ResourceId.wrap(abi.decode(args, (bytes32)));
Expand Down
Expand Up @@ -29,9 +29,7 @@ contract UniqueEntityModule is Module {
function installRoot(bytes memory args) public {
// Naive check to ensure this is only installed once
// TODO: only revert if there's nothing to do
if (InstalledModules.getModuleAddress(getName(), keccak256(args)) != address(0)) {
revert Module_AlreadyInstalled();
}
requireNotInstalled(getName(), args);

IBaseWorld world = IBaseWorld(_world());

Expand All @@ -54,9 +52,7 @@ contract UniqueEntityModule is Module {
function install(bytes memory args) public {
// Naive check to ensure this is only installed once
// TODO: only revert if there's nothing to do
if (InstalledModules.getModuleAddress(getName(), keccak256(args)) != address(0)) {
revert Module_AlreadyInstalled();
}
requireNotInstalled(getName(), args);

IBaseWorld world = IBaseWorld(_world());

Expand Down
22 changes: 22 additions & 0 deletions packages/world/src/Module.sol
Expand Up @@ -4,6 +4,7 @@ pragma solidity >=0.8.21;
import { WorldContextConsumer } from "./WorldContext.sol";
import { IModule, MODULE_INTERFACE_ID } from "./IModule.sol";
import { IERC165, ERC165_INTERFACE_ID } from "./IERC165.sol";
import { InstalledModules } from "./codegen/tables/InstalledModules.sol";

/**
* @title Module
Expand All @@ -21,4 +22,25 @@ abstract contract Module is IModule, WorldContextConsumer {
) public pure virtual override(IERC165, WorldContextConsumer) returns (bool) {
return interfaceId == MODULE_INTERFACE_ID || interfaceId == ERC165_INTERFACE_ID;
}

/**
* @dev Check if a module with the given name and arguments is installed.
* @param moduleName The name of the module.
* @param args The arguments for the module installation.
* @return true if the module is installed, false otherwise.
*/
function isInstalled(bytes16 moduleName, bytes memory args) internal view returns (bool) {
return InstalledModules.get(moduleName, keccak256(args)) != address(0);
}

/**
* @dev Revert if the module with the given name and arguments is already installed.
* @param moduleName The name of the module.
* @param args The arguments for the module installation.
*/
function requireNotInstalled(bytes16 moduleName, bytes memory args) internal view {
if (isInstalled(moduleName, args)) {
revert Module_AlreadyInstalled();
}
}
}

0 comments on commit eb384bb

Please sign in to comment.