# MUD modules 101 (1/2) Installation

By [0xshanks](https://paragraph.com/@nakaj1ma) · 2024-03-07

---

TL: DR
======

*   The module is a script to install reusable Systems and Store Hooks for the World.
    
*   The advantages of the module are that an off-chain indexer can be used, and loosely coupled stores can be maintained by separating by Namespace.
    
*   Conversely, the disadvantages include the need to write separate modules to customize official samples and the lack of upgradability at present.
    
*   The installation of erc20-puppet and erc721-puppet can be confusing so I will explain with examples.
    

Note: MUD is currently under development and the content of this article may be out of date. This article is supported up to version 2.0.0-next.17

Abstract
========

To quote from the Modules description in the official documentation

> Modules are onchain installation scripts that create resources and their associated configuration when called by a `World`. This is somewhat similar to one of the use cases for [foundry scripts](https://book.getfoundry.sh/tutorials/solidity-scripting), except that modules are deployed onchain and can be used by any `World` on the same chain.

The resources mentioned here include not only System but also Store hooks, etc. The official Module files are structured as follows

*   Module definition with register script functionality
    
*   System or Store Hooks definition
    
*   Table definitions (mud.config.ts)
    
*   Other constant definitions such as errors, events, table IDs, etc.
    

Use cases
=========

Possible use cases are

*   When using and distributing the ERC standard Contract as a System
    
*   When using and distributing the versatile Store hooks
    

According to the official documentation

> The common use for a module is to add functionality to a `World`. In most cases we expect that a module would:
> 
> 1.  Create a [namespace](https://file+.vscode-resource.vscode-cdn.net/Users/ibuki.nakajima/Workspace/Private/mud/docs/pages/world/namespaces-access-control.mdx) for the new functionality.
>     
> 2.  Create the [tables](https://file+.vscode-resource.vscode-cdn.net/Users/ibuki.nakajima/Workspace/Private/mud/docs/pages/world/.tables) and `System`s\\ for the new functionality.
>     
> 3.  Create any access permissions required (beyond the default, which is that a `System` has access to its own namespace).
>     
> 4.  Either assign the ownership of the new namespace to an entity that would administer it (a user, a multisig, etc.) or burn it by assigning the namespace ownership to `address(0)`.
>     

The official sample contains seven modules, including the following

— Store Hook —-

*   `[KeysInTable](https://github.com/latticexyz/mud/tree/main/packages/world-modules/src/modules/keysintable)` - automatically tracks the keys in a table to make them enumerable.
    
*   `[KeysWithValue](https://github.com/latticexyz/mud/tree/main/packages/world-modules/src/modules/keyswithvalue)` - automatically tracks a reverse mapping for a table that maps a value hash to a list of keys with this value.
    

— System —

*   `[Puppet](https://github.com/latticexyz/mud/tree/main/packages/world-modules/src/modules/puppet)` - installs the PuppetDelegationControl to allow creating Puppet contracts, eg used by the ERC20 and ERC721 modules.
    
*   `[ERC20](https://github.com/latticexyz/mud/tree/main/packages/world-modules/src/modules/erc20-puppet)` - installs an ERC20 token into a namespace in a World.
    
*   `[ERC721](https://github.com/latticexyz/mud/tree/main/packages/world-modules/src/modules/erc721-puppet)` - installs an ERC721 token into a namespace in a World.
    
*   `[UniqueEntity](https://github.com/latticexyz/mud/tree/main/packages/world-modules/src/modules/uniqueentity)` - add methods to get a unique entity ID.
    
*   `[Standard Delegations](https://github.com/latticexyz/mud/tree/main/packages/world-modules/src/modules/std-delegations)` - add delegations limited by time or number of calls.
    

Pros. Cons.
===========

Pros. to install as a module are as follows

*   If installed as a module in World, the following functions are available
    
    *   Indexing with an off-chain indexer if the table is included in the module.
        
    *   Namespace Access Control allows writing only from authorized Namespace.
        
*   When multiple Systems with the same functionality are installed, the Store is loosely coupled due to Namespace separation.
    

The disadvantages at present are as follows

*   For example, if you want to create an ERC721ExpandedSystem by inheriting from ERC721System, you cannot use the code of ERC721Module, so you need to define a separate Module if you want to customize the System.
    
*   Installed modules cannot be upgraded, so it is necessary to reinstall them in a different namespace and avoid using older versions.
    

Module installation
===================

The official documentation states

Modules can be installed using `[World.installModule(address moduleAddress, bytes memory initData)](https://github.com/latticexyz/mud/blob/ main/packages/world/src/modules/init/implementations/ModuleInstallationSystem.sol#L17-L37)`

but additional work is required for erc20-puppet and erc721-puppet, which is easily confused and explained. Here, we will use erc721-puppet as an example.

For a more practical example, Sky Strife's PostDeploy script may be helpful.

[https://github.com/latticexyz/skystrife-public/blob/main/packages/contracts/script/PostDeploy.s.sol](https://github.com/latticexyz/skystrife-public/blob/main/packages/contracts/script/PostDeploy.s.sol)

This is an example of an install script for erc721-puppet.

    // SPDX-License-Identifier: MIT
    pragma solidity >=0.8.24;
    
    import { Script } from "forge-std/Script.sol";
    import { console } from "forge-std/console.sol";
    import { StoreSwitch } from "@latticexyz/store/src/StoreSwitch.sol";
    import { IERC721Mintable } from "@latticexyz/world-modules/src/modules/erc721-puppet/IERC721Mintable.sol";
    import { PuppetModule } from "@latticexyz/world-modules/src/modules/puppet/PuppetModule.sol";
    import { ERC721MetadataData } from "@latticexyz/world-modules/src/modules/erc721-puppet/tables/ERC721Metadata.sol";
    import { registerERC721 } from "@latticexyz/world-modules/src/modules/erc721-puppet/registerERC721.sol";
    import { ResourceId, WorldResourceIdLib } from "@latticexyz/world/src/WorldResourceId.sol";
    
    bytes14 constant NAMESPACE = "TestNS";
    
    contract InstallERC20Example is Script {
      function run(address worldAddress) external {
        IWorld world = IWorld(worldAddress);
        StoreSwitch.setStoreAddress(worldAddress);
        uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
    
        vm.startBroadcast(deployerPrivateKey);
    
        world.installModule(new PuppetModule(), new bytes(0));
    
        IERC721Mintable token = registerERC721(
          world,
          NAMESPACE,
          ERC721MetadataData({ name: "Test", symbol: "TEST", baseURI: "http://test" })
        );
    
        // Transfer namespace to World
        ResourceId namespaceId = WorldResourceIdLib.encodeNamespace(NAMESPACE);
        world.transferOwnership(namespaceId, worldAddress);
    
        // Store token address
    
        vm.stopBroadcast();
      }
    }
    

I'll also include an example of a shell that runs this script.

    $ forge script InstallERC20Example --sig 'run(address)' --rpc-url=$RPC_HTTP_URL $YOUR_WORLD_ADDRESS --broadcast
    

The meaning of each row is explained below.

Since there is a possibility that more than one of these ERC standards may be installed in the world, each of them should have a unique Namespace to avoid duplication of Tables. For example, when using a utility token and a governance token such as ERC20.

    bytes14 constant NAMESPACE = "TestNS";
    

To use erc721-puppet, you need to install the Puppet module in advance, so do the following.

    world.installModule(new PuppetModule(), new bytes(0));
    

The puppet module provides more detailed access control and logging capabilities and is described in the official documentation as follows

> [Puppet](https://github.com/latticexyz/mud/tree/main/packages/world-modules/src/modules/puppet) - installs the PuppetDelegationControl to allow creating Puppet contracts, eg used by the ERC20 and ERC721 modules.

Then call `registerERC721()`

    IERC721Mintable token = registerERC721(
      world,
      NAMESPACE,
      ERC721MetadataData({ name: "Test", symbol: "TEST", baseURI: "http://test" })
    );
    

Inside `registerERC721()`, `world.installModule()` is called to install the module in the world.

    function registerERC721(
      IBaseWorld world,
      bytes14 namespace,
      ERC721MetadataData memory metadata
    ) returns (IERC721Mintable token) {
      // Get the ERC721 module
      ERC721Module erc721Module = ERC721Module(NamespaceOwner.get(MODULE_NAMESPACE_ID));
      if (address(erc721Module) == address(0)) {
        erc721Module = new ERC721Module();
      }
    
      // Install the ERC721 module with the provided args
      world.installModule(erc721Module, abi.encode(namespace, metadata));
    
      // Return the newly created ERC721 token
      token = IERC721Mintable(ERC721Registry.get(ERC721_REGISTRY_TABLE_ID, WorldResourceIdLib.encodeNamespace(namespace)));
    }
    

Next, transfer the ownership rights of Namespace to the world.

    ResourceId namespaceId = WorldResourceIdLib.encodeNamespace(NAMESPACE);
    world.transferOwnership(namespaceId, worldAddress);
    

The following is an excerpt from the `ERC721System` to be Installed by this script to illustrate why the transfer of permissions is necessary.

For example, `mint()` calls `_requireOwner()`, which checks whether `_msgSender()` has access privileges to `ERC721System`. `transferOwnership()` in the installation script passes access privileges to `worldAddress`, so that access from the world is allowed and all others are reverted.

    contract ERC721System is IERC721Mintable, System, PuppetMaster {
      // ...
        
      function mint(address to, uint256 tokenId) public virtual {
        _requireOwner();
        _mint(to, tokenId);
      }
    
      function _requireOwner() internal view {
        AccessControlLib.requireOwner(SystemRegistry.get(address(this)), _msgSender());
      }
    
      // ...
    }
    

Finally, although not included in the script, save the address of the installed token so that it can be called from the system, etc.

Finally
-------

In this article, I have given an overview of modules and some additional installation information. In the next article, I will write about how to customize modules.

---

*Originally published on [0xshanks](https://paragraph.com/@nakaj1ma/mud-modules-101-1-2-installation)*
