Store hooks
Store hooks allow you to react to state changes onchain.
There are many ways to interact with tables, but they all boil down to four types of state changes:
- A full record (static and dynamic length data) is set (
SetRecord
) - A part of the static length data of a record is changed (
SpliceStaticData
) - A part of the dynamic length data of a record is changed (
SpliceDynamicData
) - A full record is deleted (
DeleteRecord
)
By registering a store hook contract on a table, arbitrary logic can be triggered before
or after
each of these state changes.
Store hook contract
A store hook contract is a contract that implements the IStoreHook
interface.
After the store hook contract is registered, Store
calls the enabled hook methods on each state change.
Example: a hook that creates a lookup table of existing values in a table
import { StoreHook } from "@latticexyz/store/src/StoreHook.sol";
import { Exists } from "./codegen/tables/Exists.sol";
contract CustomHook is StoreHook {
function onBeforeSetRecord(
ResourceId tableId,
bytes32[] memory keyTuple,
bytes memory staticData,
EncodedLengths encodedLengths,
bytes memory dynamicData,
FieldLayout
) public override {
Exists.set(keccak256(abi.encodePacked(staticData, encodedLengths, dynamicData)), true);
}
}
Hook registration
Hooks are registered via the StoreCore
library or IStore
interface.
It is possible to select the type of state change that should trigger a hook and the time (before or after) with a bitmap passed during the registration.
/// @dev Flag to enable the `onBeforeSetRecord` hook.
uint8 constant BEFORE_SET_RECORD = 1 << 0;
/// @dev Flag to enable the `afterSetRecord` hook.
uint8 constant AFTER_SET_RECORD = 1 << 1;
/// @dev Flag to enable the `beforeSpliceStaticData` hook.
uint8 constant BEFORE_SPLICE_STATIC_DATA = 1 << 2;
/// @dev Flag to enable the `afterSpliceStaticData` hook.
uint8 constant AFTER_SPLICE_STATIC_DATA = 1 << 3;
/// @dev Flag to enable the `beforeSpliceDynamicData` hook.
uint8 constant BEFORE_SPLICE_DYNAMIC_DATA = 1 << 4;
/// @dev Flag to enable the `afterSpliceDynamicData` hook.
uint8 constant AFTER_SPLICE_DYNAMIC_DATA = 1 << 5;
/// @dev Flag to enable the `beforeDeleteRecord` hook.
uint8 constant BEFORE_DELETE_RECORD = 1 << 6;
/// @dev Flag to enable the `afterDeleteRecord` hook.
uint8 constant AFTER_DELETE_RECORD = 1 << 7;
Example: registering a custom store hook that is triggered before a record is set and before a record is deleted
import { StoreCore } from "@latticexyz/store/src/StoreCore.sol";
import { Resourceid } from "@latticexyz/store/src/ResourceId.sol";
import {
BEFORE_SET_RECORD,
AFTER_DELETE_RECORD
} from "@latticexyz/store/src/storeHookTypes.sol";
ResourceId TABLE_ID = ... // table ID to watch for state changes
StoreCore.registerStoreHook({
tableId: TABLE_ID,
hookAddress: address(new CustomHook()),
enabledHooksBitmap: BEFORE_SET_RECORD | AFTER_SET_RECORD
});