Contract Systems

Source for Primodium System Interfaces

Primodium Systems in World Extensions

There are two ways to reference Primodium systems in a world extension:

  • Install the @primodiumxyz/contracts package and import the system interfaces directly.
  • Copy the system interfaces to your world extension project and import them from there.

Installing the @primodiumxyz/contracts package

If your world extension is a Foundry (opens in a new tab) project, you can install the @primodiumxyz/contracts package as a dependency. In the root directory of your world extension project, run the following command:

pnpm install @primodiumxyz/contracts

Make sure the project's foundry.toml references node_modules as a package directory.

foundry.toml
[profile.default]
# ...
allow_paths = [
  # pnpm symlinks to the project root's node_modules
  "../../node_modules",
  # template uses linked mud packages from within the mud monorepo
  "../../../../packages",
  # projects created from this template and using linked mud packages
  "../../../mud/packages",
]
#...

Then, update remappings.txt to point to the installed package. For example, if you are using the @primodiumxyz/contracts package in a world extension, update the remappings.txt file as follows:

remappings.txt
primodium=node_modules/@primodiumxyz/contracts/src/codegen/

The ReadDemo world extension example references system interfaces directly from @primodiumxyz/contracts. The following snipped highlights lines on how to import and reference the Home table.

examples/ReadDemo/packages/contracts/src/systems/ReadDemoSystem.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.24;
 
import { System } from "@latticexyz/world/src/System.sol";
import { StoreSwitch } from "@latticexyz/store/src/StoreSwitch.sol";
 
import { Home } from "primodium/tables/Home.sol";
import { Level } from "primodium/tables/Level.sol";
 
// We're building a System, to extend the System contract
contract ReadDemoSystem is System {
  function readMainBaseLevel() public returns (uint32) {
    // we want to read from the Primodium World, not the Extension World
    StoreSwitch.setStoreAddress(_world());
 
    // Get the players ID
    // msg.sender in this case will be the World. We want the player instead.
    // use _msgSender() to get the address of the player calling the function
    bytes32 playerEntity = bytes32(uint256(uint160(_msgSender())));
 
    // Home.get is dual purpose. Using it on a player ID returns their home asteroid ID
    // Using it on an asteroid ID returns the base building ID of that asteroid
 
    // get the ID of the players home base asteroid
    bytes32 asteroidEntity = Home.get(playerEntity);
 
    // get the ID of the base building on the players home base asteroid
    bytes32 baseEntity = Home.get(asteroidEntity);
 
    // get and return the level of the base building
    return uint32(Level.get(baseEntity));
  }
}

Directly Referencing System Interfaces

The BuildingUpgradeBounty world extension example requires a reference to functions in the Primodium UpgradeBuildingSystem, which handles building upgrades. The following steps demonstrate how to reference the UpgradeBuildingSystem in a world extension and successfully its function upgradeBuilding().

First, copy the following folders to your Primodium extension project:

In the BuildingUpgradeBounty example, the above files are copied to /examples/BuildingUpgradeBounty/packages/contracts/src/primodium-codegen (opens in a new tab).

Locate the function you want to call in the generated system interfaces. In this case, we want to call the upgrade Pri_11__upgradeBuilding(), whose function signature is obtained from the generated IUpgradeBuildingSystem.

@primodiumxyz/contracts/src/codegen/world/IUpgradeBuildingSystem.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.24;
 
/* Autogenerated file. Do not edit manually. */
 
/**
 * @title IUpgradeBuildingSystem
 * @author MUD (https://mud.dev) by Lattice (https://lattice.xyz)
 * @dev This interface is automatically generated from the corresponding system contract. Do not edit manually.
 */
interface IUpgradeBuildingSystem {
  function Pri_11__upgradeBuilding(bytes32 buildingEntity) external;
}

The upgradeBuilding()function in BuildSystem can then be called as follows, with UpgradeBuildingS being the system ID of the Primodium UpgradeBuildingSystem cut off at 16 bytes. See footguns for more details.

examples/BuildingUpgradeBounty/packages/contracts/src/systems/UpgrBounSystem.sol
import { IWorld as IPrimodiumWorld } from "../primodium-codegen/world/IWorld.sol";
 
contract UpgrBounSystem is System {
  /**
   * @dev Upgrades a specified building using the bounty published by the given address.
   * @param bountyPublisherAddress The address of the bounty publisher.
   * @param buildingEntityParam The building to upgrade.
   * @return newBuildingEntity The new building entity.
   */
  function upgradeForBounty(
    address bountyPublisherAddress,
    bytes32 buildingEntityParam
  ) public returns (bytes memory newBuildingEntity) {
    // ...
    // Call the upgradeBuilding function from the World contract
    ResourceId upgradeBuildingSystemId = WorldResourceIdLib.encode(RESOURCE_SYSTEM, PRIMODIUM_NAMESPACE, "UpgradeBuildingS");
 
    newBuildingEntity = IPrimodiumWorld(_world()).callFrom(
      bountyPublisherAddress,
      upgradeBuildingSystemId,
      abi.encodeWithSignature("upgradeBuilding(bytes32)", buildingEntity)
    );
    // ...
  }
}