Guides
MUD for Solidity Programmers

MUD for Solidity Programmers

To properly use MUD you need to do some things differently than the way you do them normally in Solidity.

State variables

A System contract should not store any internal state, but only interact with World state via Tables.

  • By concentrating all the state information in a single contract (the World) we make it easier to apply access control, execute hooks when data changes, etc.
  • It is a lot easier to upgrade functionality when you do not need to deal with storage because the contract does not have a state.
  • The same System can be used by multiple Worlds.

Instead of using state variables (opens in a new tab) you can achieve the same functionality with tables.

Value-type (string, uint, etc.)

Value-type (opens in a new tab) state variables such as bool, uint256, and string are represented by a singleton table, a table with no key that only contains a single record.

Code example
Solidity
contract Example {
  string name;
}
MUD
import { defineWorld } from "@latticexyz/world";
 
export default defineWorld({
  namespace: "app",
  tables: {
    Name: {
      schema: {
        name: "string",
      },
      key: [],
    },
  },
});

Structures

A struct (opens in a new tab) state variable wraps multiple variables together. You can create a singleton table with multiple value fields.

Code example
Solidity
contract Example {
  struct Person {
    string name;
    address personalWallet;
    uint balance;
  }
  Person deployer;
}
MUD
import { defineWorld } from "@latticexyz/world";
 
export default defineWorld({
  namespace: "app",
  tables: {
    Deployer: {
      schema: {
        personalWallet: "address",
        balance: "uint256",
        name: "string",
      },
      key: [],
    },
  },
});

Mappings

A mapping type (opens in a new tab) maps between a key type and a value type, which is pretty much the same as what MUD tables do.

Single key mapping
Solidity
contract Example {
  mapping(address => uint) public balances;
}
MUD
import { defineWorld } from "@latticexyz/world";
 
export default defineWorld({
  namespace: "app",
  tables: {
    Balances: {
      schema: {
        owner: "address",
        balance: "uint256",
      },
      key: ["owner"],
    },
  },
});
Multiple key mapping
Solidity
contract Example {
  mapping(address => mapping(address => uint256)) public allowances;
}
MUD
import { defineWorld } from "@latticexyz/world";
 
export default defineWorld({
  namespace: "app",
  tables: {
    Allowances: {
      schema: {
        owner: "address",
        spender: "address",
        balance: "uint256",
      },
      key: ["owner", "spender"],
    },
  },
});
📝

MUD key fields have to be fixed-length. This means that some mappings, for example mapping(string => uint) balances;, cannot be directly translated to a MUD table.

An easy workaround is to use the hash of the string as the key instead of the string itself (which is exactly what happens under the hood of vanilla Solidity with the keys of a mapping).

Arrays

There are two ways in which MUD supports arrays.

  • Arrays of fixed-length value types, such as uint24[] or address[], are supported as table fields (opens in a new tab).

    Example
    Solidity
    contract Example {
      bool[] listOfBooleans;
    }
    MUD
    import { defineWorld } from "@latticexyz/world";
     
    export default defineWorld({
      namespace: "app",
      tables: {
        ListOfBooleans: {
          schema: {
            values: "bool[]",
          },
          key: [],
        },
      },
    });
  • An array is a mapping between an unsigned integer and the value type of the array. We can use a table with index as the key. That way, we can get around the limitation that arrays of variable-length types such as string or bytes are not supported.

    Example
    Solidity
    contract Example {
      string[] listOfNames;
    }
    MUD
    import { defineWorld } from "@latticexyz/world";
     
    export default defineWorld({
      namespace: "app",
      tables: {
        ListOfNames: {
          schema: {
            index: "uint256",
            name: "string",
          },
          key: ["index"],
        },
      },
    });

Special variables

Applications never call a System directly. All calls go through a World for state, access control, balance management, etc. As a result, some of the Solidity special variables (opens in a new tab) have to replaced with functions that obtain the relevant information.

MeaningSolidityMUD
Callermsg.sender_msgSender()
Value sent with the callmsg.value_msgValue()