# MUD modules 101 (1/2) Installation **Published by:** [0xshanks](https://paragraph.com/@nakaj1ma/) **Published on:** 2024-03-07 **URL:** https://paragraph.com/@nakaj1ma/mud-modules-101-1-2-installation ## Content TL: DRThe 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.17AbstractTo quote from the Modules description in the official documentationModules 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, 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 followsModule definition with register script functionalitySystem or Store Hooks definitionTable definitions (mud.config.ts)Other constant definitions such as errors, events, table IDs, etc.Use casesPossible use cases areWhen using and distributing the ERC standard Contract as a SystemWhen using and distributing the versatile Store hooksAccording to the official documentationThe common use for a module is to add functionality to a World. In most cases we expect that a module would:Create a namespace for the new functionality.Create the tables and Systems\ for the new functionality.Create any access permissions required (beyond the default, which is that a System has access to its own namespace).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 followsIf installed as a module in World, the following functions are availableIndexing 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 followsFor 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 installationThe 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 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 followsPuppet - 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.FinallyIn 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. ## Publication Information - [0xshanks](https://paragraph.com/@nakaj1ma/): Publication homepage - [All Posts](https://paragraph.com/@nakaj1ma/): More posts from this publication - [RSS Feed](https://api.paragraph.com/blogs/rss/@nakaj1ma): Subscribe to updates - [Twitter](https://twitter.com/0xredhair): Follow on Twitter