Upgrading worlds

The Systems can be upgraded without changing the underlying World code, using mud deploy --worldAddress. However, you can also upgrade the World contract itself if the World was deployed behind a proxy. This allows you to upgrade to a future version of MUD, but adds some gas overhead for all calls (due to one more level of indirection).

Making an upgradeable World

To make a World upgradeable, edit the mud.config.ts file and set deploy.upgradeableWorldImplementation to true.

import { defineWorld } from "@latticexyz/world";
export default defineWorld({
  deploy: {
    upgradeableWorldImplementation: true,
  tables: {
    Counter: {
      schema: {
        value: "uint32",
      key: [],

Testing if upgrades are possible

As per ERC-1967 (opens in a new tab), the storage slot 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc stores the address of the contract implementation in a proxy. So to identify if a World is deployed behind a proxy run these commands:

cast implementation $WORLD_ADDRESS

If the answer is anything other than zero, the World is behind a proxy and can be upgraded.

Performing an upgrade

To upgrade the World use these steps in a package/contracts directory:

  1. Edit .env to add WORLD_ADDRESS with the address displayed by MUD Dev Tools. For example, you might want to use this file:

    # This .env file is for demonstration purposes only.
    # This should usually be excluded via .gitignore and the env vars attached to
    # your deployment environment, but we're including this here for ease of local
    # development. Please do not commit changes to this file!
    # Enable debug logs for MUD CLI
    # Anvil default private key:
  2. Create this script in scripts/UpgradeWorld.s.sol.

    // SPDX-License-Identifier: MIT
    pragma solidity >=0.8.24;
    import { Script } from "forge-std/Script.sol";
    import { console } from "forge-std/console.sol";
    import { World } from "@latticexyz/world/src/World.sol";
    import { WorldProxy } from "@latticexyz/world/src/WorldProxy.sol";
    contract SetImplementation is Script {
      function run() external {
        uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
        address proxyAddress = vm.envAddress("WORLD_ADDRESS");
        // Deploy new world implementation
        World newWorld = new World();
        console.log("proxy:", proxyAddress);
        console.log("new World:", address(newWorld));
  3. Run the script.

    forge script script/UpgradeWorld.s.sol --rpc-url http://localhost:8545 --broadcast
  4. See the new address.

    source .env
    cast implementation $WORLD_ADDRESS