System hooks
This page is about hooks that are called before or after a System
call. You can also use store
hooks that are called whenever information in a table is modified, regardless of the source of
the change.
The namespace owner can register (opens in a new tab) hooks that are called before and/or after calls to a specific System
.
System hook contracts
A system hook contract is a contract that implements the ISystemHook
(opens in a new tab) interface.
This interface requires the implementation of two functions.
onBeforeCallSystem
, which is called before the actual call.onAfterCallSystem
, which is called afterward.supportsInterface
, which is part of IEP-165 (opens in a new tab) used to specify which interfaces are supported by a contract.
To have the correct supportsInterface
you can inherit from SystemHook
(opens in a new tab).
Sample code
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.24;
import { Script } from "forge-std/Script.sol";
import { console } from "forge-std/console.sol";
import { IWorld } from "../src/codegen/world/IWorld.sol";
import { SystemHook } from "@latticexyz/world/src/SystemHook.sol";
import { ResourceId, WorldResourceIdLib, WorldResourceIdInstance } from "@latticexyz/world/src/WorldResourceId.sol";
import { RESOURCE_SYSTEM } from "@latticexyz/world/src/worldResourceTypes.sol";
import { BEFORE_CALL_SYSTEM, AFTER_CALL_SYSTEM } from "@latticexyz/world/src/systemHookTypes.sol";
contract JustSayNo is SystemHook {
function onBeforeCallSystem(address msgSender, ResourceId systemId, bytes memory callData) external {
return;
}
function onAfterCallSystem(address msgSender, ResourceId systemId, bytes memory callData) external {
revert("Just say no");
}
}
contract SystemHookDeploy is Script {
function run() external {
address worldAddress = 0xC14fBdb7808D9e2a37c1a45b635C8C3fF64a1cc1;
// Load the private key from the `PRIVATE_KEY` environment variable (in .env)
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
// Start broadcasting transactions from the deployer account
vm.startBroadcast(deployerPrivateKey);
// Deploy JustSayNo
JustSayNo justSayNo = new JustSayNo();
console.log(address(justSayNo));
ResourceId systemId = WorldResourceIdLib.encode({ typeId: RESOURCE_SYSTEM, namespace: "", name: "TasksSystem" });
IWorld(worldAddress).registerSystemHook(systemId, justSayNo, AFTER_CALL_SYSTEM);
vm.stopBroadcast();
}
}
Explanation
import { SystemHook } from "@latticexyz/world/src/SystemHook.sol";
The system hook contract inherits from SystemHook
.
import { ResourceId, WorldResourceIdLib, WorldResourceIdInstance } from "@latticexyz/world/src/WorldResourceId.sol";
import { RESOURCE_SYSTEM } from "@latticexyz/world/src/worldResourceTypes.sol";
We need to create the resource of the System
for which we register a hook.
import { BEFORE_CALL_SYSTEM, AFTER_CALL_SYSTEM } from "@latticexyz/world/src/systemHookTypes.sol";
We need these constants to specify when the hook applies.
contract JustSayNo is SystemHook {
function onBeforeCallSystem(address msgSender, ResourceId systemId, bytes memory callData) external {
return ;
}
function onAfterCallSystem(address msgSender, ResourceId systemId, bytes memory callData) external {
This is the system hook contract. It has two functions:
onBeforeCallSystem
, which is executed before the callonAfterCallSystem
, which is executed after the call
Both functions get the sender, the system being called, and the call data it receives.
revert("Just say no");
}
}
This hook disables the System
by causing all calls to revert.
contract SystemHookDeploy is Script {
function run() external {
...
This is the function that the script executes.
// Deploy JustSayNo
JustSayNo justSayNo = new JustSayNo();
Deploy the system hook contract.
ResourceId systemId = WorldResourceIdLib.encode(
{ typeId: RESOURCE_SYSTEM,
namespace: "",
name: "TasksSystem"
});
Create the resource ID for the System
we are disabling.
IWorld(worldAddress)
.registerSystemHook(systemId, justSayNo, AFTER_CALL_SYSTEM);
}
}
Register the system hook.