Releases: latticexyz/mud
@latticexyz/[email protected]
@latticexyz/[email protected]
[email protected]
[email protected]
[email protected]
Minor Changes
-
#1482
07dd6f32
Thanks @alvrs! - Renamed all occurrences ofschema
where it is used as "value schema" tovalueSchema
to clearly distinguish it from "key schema".
The only breaking change for users is the change fromschema
tovalueSchema
inmud.config.ts
.// mud.config.ts export default mudConfig({ tables: { CounterTable: { keySchema: {}, - schema: { + valueSchema: { value: "uint32", }, }, } }
-
#1483
83583a50
Thanks @holic! - Templates now useout
for theirforge build
artifacts, including ABIs. If you have a project created from a previous template, you can update yourpackages/contracts/package.json
with:- "build:abi": "rimraf abi && forge build --extra-output-files abi --out abi --skip test script MudTest.sol", - "build:abi-ts": "mud abi-ts --input 'abi/IWorld.sol/IWorld.abi.json' && prettier --write '**/*.abi.json.d.ts'", + "build:abi": "forge clean && forge build --skip test script", + "build:abi-ts": "mud abi-ts && prettier --write '**/*.abi.json.d.ts'",
And your
packages/client/src/mud/setupNetwork
with:- import IWorldAbi from "contracts/abi/IWorld.sol/IWorld.abi.json"; + import IWorldAbi from "contracts/out/IWorld.sol/IWorld.abi.json";
-
#1473
92de5998
Thanks @holic! - Bump Solidity version to 0.8.21 -
#1354
331dbfdc
Thanks @dk1a! - We've updated Store events to be "schemaless", meaning there is enough information in each event to only need to operate on the bytes of each record to make an update to that record without having to first decode the record by its schema. This enables new kinds of indexers and sync strategies.As such, we've replaced
blockStorageOperations# Change Log with
storedBlockLogs# Change Log, a stream of simplified Store event logs after they've been synced to the configured storage adapter. These logs may not reflect exactly the events that are on chain when e.g. hydrating from an indexer, but they will still allow the client to "catch up" to the on-chain state of your tables. -
#1558
bfcb293d
Thanks @alvrs! - What used to be known asephemeral
table is now calledoffchain
table.
The previousephemeral
tables only supported anemitEphemeral
method, which emitted aStoreSetEphemeralRecord
event.Now
offchain
tables support all regular table methods, except partial operations on dynamic fields (push
,pop
,update
).
Unlike regular tables they don't store data on-chain but emit the same events as regular tables (StoreSetRecord
,StoreSpliceStaticData
,StoreDeleteRecord
), so their data can be indexed by offchain indexers/clients.- EphemeralTable.emitEphemeral(value); + OffchainTable.set(value);
Patch Changes
-
#1318
ac508bf1
Thanks @holic! - Renamed the default filename of generated user types fromTypes.sol
tocommon.sol
and the default filename of the generated table index file fromTables.sol
toindex.sol
.Both can be overridden via the MUD config:
export default mudConfig({ /** Filename where common user types will be generated and imported from. */ userTypesFilename: "common.sol", /** Filename where codegen index will be generated. */ codegenIndexFilename: "index.sol", });
Note:
userTypesFilename
was renamed fromuserTypesPath
and.sol
is not appended automatically anymore but needs to be part of the provided filename.To update your existing project, update all imports from
Tables.sol
toindex.sol
and all imports fromTypes.sol
tocommon.sol
, or override the defaults in your MUD config to the previous values.- import { Counter } from "../src/codegen/Tables.sol"; + import { Counter } from "../src/codegen/index.sol"; - import { ExampleEnum } from "../src/codegen/Types.sol"; + import { ExampleEnum } from "../src/codegen/common.sol";
-
#1581
cea754dd
Thanks @alvrs! - - The externalsetRecord
anddeleteRecord
methods ofIStore
no longer accept aFieldLayout
as input, but load it from storage instead.
This is to prevent invalidFieldLayout
values being passed, which could cause the onchain state to diverge from the indexer state.
However, the internalStoreCore
library still exposes asetRecord
anddeleteRecord
method that allows aFieldLayout
to be passed.
This is becauseStoreCore
can only be used internally, so theFieldLayout
value can be trusted and we can save the gas for accessing storage.interface IStore { function setRecord( ResourceId tableId, bytes32[] calldata keyTuple, bytes calldata staticData, PackedCounter encodedLengths, bytes calldata dynamicData, - FieldLayout fieldLayout ) external; function deleteRecord( ResourceId tableId, bytes32[] memory keyTuple, - FieldLayout fieldLayout ) external; }
-
The
spliceStaticData
method andStore_SpliceStaticData
event ofIStore
andStoreCore
no longer includedeleteCount
in their signature.
This is because when splicing static data, the data afterstart
is always overwritten withdata
instead of being shifted, sodeleteCount
is always the length of the data to be written.event Store_SpliceStaticData( ResourceId indexed tableId, bytes32[] keyTuple, uint48 start, - uint40 deleteCount, bytes data ); interface IStore { function spliceStaticData( ResourceId tableId, bytes32[] calldata keyTuple, uint48 start, - uint40 deleteCount, bytes calldata data ) external; }
-
The
updateInField
method has been removed fromIStore
, as it's almost identical to the more generalspliceDynamicData
.
If you're manually callingupdateInField
, here is how to upgrade tospliceDynamicData
:- store.updateInField(tableId, keyTuple, fieldIndex, startByteIndex, dataToSet, fieldLayout); + uint8 dynamicFieldIndex = fieldIndex - fieldLayout.numStaticFields(); + store.spliceDynamicData(tableId, keyTuple, dynamicFieldIndex, uint40(startByteIndex), uint40(dataToSet.length), dataToSet);
-
All other methods that are only valid for dynamic fields (
pushToField
,popFromField
,getFieldSlice
)
have been renamed to make this more explicit (pushToDynamicField
,popFromDynamicField
,getDynamicFieldSlice
).Their
fieldIndex
parameter has been replaced by adynamicFieldIndex
parameter, which is the index relative to the first dynamic field (i.e.dynamicFieldIndex
=fieldIndex
-numStaticFields
).
TheFieldLayout
parameter has been removed, as it was only used to calculate thedynamicFieldIndex
in the method.interface IStore { - function pushToField( + function pushToDynamicField( ResourceId tableId, bytes32[] calldata keyTuple, - uint8 fieldIndex, + uint8 dynamicFieldIndex, bytes calldata dataToPush, - FieldLayout fieldLayout ) external; - function popFromField( + function popFromDynamicField( ResourceId tableId, bytes32[] calldata keyTuple, - uint8 fieldIndex, + uint8 dynamicFieldIndex, uint256 byteLengthToPop, - FieldLayout fieldLayout ) external; - function getFieldSlice( + function getDynamicFieldSlice( ResourceId tableId, bytes32[] memory keyTuple, - uint8 fieldIndex, + uint8 dynamicFieldIndex, - FieldLayout fieldLayout, uint256 start, uint256 end ) external view returns (bytes memory data); }
-
IStore
has a newgetDynamicFieldLength
length method, which returns the byte length of the given dynamic field and doesn't require theFieldLayout
.IStore { + function getDynamicFieldLength( + ResourceId tableId, + bytes32[] memory keyTuple, + uint8 dynamicFieldIndex + ) external view returns (uint256); }
-
IStore
now has additional overloads forgetRecord
,getField
,getFieldLength
andsetField
that don't require aFieldLength
to be passed, but instead load it from storage. -
IStore
now exposessetStaticField
andsetDynamicField
to save gas by avoiding the dynamic in...
-
@latticexyz/[email protected]
Major Changes
-
#1606
77dce993
Thanks @holic! - Moves World interfaces and factories files for consistency with our other packages.If you import any World interfaces or factories directly, you'll need to update the import path:
- import { IBaseWorld } from "@latticexyz/world/src/interfaces/IBaseWorld.sol"; + import { IBaseWorld } from "@latticexyz/world/src/IBaseWorld.sol";
- import { IBaseWorld } from "@latticexyz/world/src/factories/WorldFactory.sol"; + import { IBaseWorld } from "@latticexyz/world/src/WorldFactory.sol";
-
#1563
748f4588
Thanks @alvrs! - AllWorld
methods now revert if theWorld
calls itself.
TheWorld
should never need to externally call itself, since all internal table operations happen via library calls, and all root system operations happen via delegate call.It should not be possible to make the
World
call itself as an external actor.
If it were possible to make theWorld
call itself, it would be possible to write to internal tables that only theWorld
should have access to.
As this is a very important invariance, we made it explicit in a requirement check in everyWorld
method, rather than just relying on making it impossible to trigger theWorld
to call itself.This is a breaking change for modules that previously used external calls to the
World
in theinstallRoot
method.
In theinstallRoot
method, theWorld
can only be called viadelegatecall
, and table operations should be performed via the internal table methods (e.g._set
instead ofset
).Example for how to replace external calls to
world
in root systems / root modules (installRoot
) withdelegatecall
:+ import { revertWithBytes } from "@latticexyz/world/src/revertWithBytes.sol"; - world.grantAccess(tableId, address(hook)); + (bool success, bytes memory returnData) = address(world).delegatecall( + abi.encodeCall(world.grantAccess, (tableId, address(hook))) + ); + if (!success) revertWithBytes(returnData);
-
#1592
c07fa021
Thanks @alvrs! - Tables and interfaces in theworld
package are now generated to thecodegen
folder.
This is only a breaking change if you imported tables or codegenerated interfaces from@latticexyz/world
directly.
If you're using the MUD CLI, the changed import paths are already integrated and no further changes are necessary.- import { IBaseWorld } from "@latticexyz/world/src/interfaces/IBaseWorld.sol"; + import { IBaseWorld } from "@latticexyz/world/src/codegen/interfaces/IBaseWorld.sol";
-
#1354
331dbfdc
Thanks @dk1a! - We've updated Store events to be "schemaless", meaning there is enough information in each event to only need to operate on the bytes of each record to make an update to that record without having to first decode the record by its schema. This enables new kinds of indexers and sync strategies.If you've written your own sync logic or are interacting with Store calls directly, this is a breaking change. We have a few more breaking protocol changes upcoming, so you may hold off on upgrading until those land.
If you are using MUD's built-in tooling (table codegen, indexer, store sync, etc.), you don't have to make any changes except upgrading to the latest versions and deploying a fresh World.
-
The
data
field in eachStoreSetRecord
andStoreEphemeralRecord
has been replaced with three new fields:staticData
,encodedLengths
, anddynamicData
. This better reflects the on-chain state and makes it easier to perform modifications to the raw bytes. We recommend storing each of these fields individually in your off-chain storage of choice (indexer, client, etc.).- event StoreSetRecord(bytes32 tableId, bytes32[] keyTuple, bytes data); + event StoreSetRecord(bytes32 tableId, bytes32[] keyTuple, bytes staticData, bytes32 encodedLengths, bytes dynamicData); - event StoreEphemeralRecord(bytes32 tableId, bytes32[] keyTuple, bytes data); + event StoreEphemeralRecord(bytes32 tableId, bytes32[] keyTuple, bytes staticData, bytes32 encodedLengths, bytes dynamicData);
-
The
StoreSetField
event is now replaced by two new events:StoreSpliceStaticData
andStoreSpliceDynamicData
. Splicing allows us to perform efficient operations like push and pop, in addition to replacing a field value. We use two events because updating a dynamic-length field also requires updating the record'sencodedLengths
(aka PackedCounter).- event StoreSetField(bytes32 tableId, bytes32[] keyTuple, uint8 fieldIndex, bytes data); + event StoreSpliceStaticData(bytes32 tableId, bytes32[] keyTuple, uint48 start, uint40 deleteCount, bytes data); + event StoreSpliceDynamicData(bytes32 tableId, bytes32[] keyTuple, uint48 start, uint40 deleteCount, bytes data, bytes32 encodedLengths);
Similarly, Store setter methods (e.g.
setRecord
) have been updated to reflect thedata
tostaticData
,encodedLengths
, anddynamicData
changes. We'll be following up shortly with Store getter method changes for more gas efficient storage reads. -
-
#1527
759514d8
Thanks @holic! - Moved the registration of store hooks and systems hooks to bitmaps with bitwise operator instead of a struct.- import { StoreHookLib } from "@latticexyz/src/StoreHook.sol"; + import { + BEFORE_SET_RECORD, + BEFORE_SET_FIELD, + BEFORE_DELETE_RECORD + } from "@latticexyz/store/storeHookTypes.sol"; StoreCore.registerStoreHook( tableId, subscriber, - StoreHookLib.encodeBitmap({ - onBeforeSetRecord: true, - onAfterSetRecord: false, - onBeforeSetField: true, - onAfterSetField: false, - onBeforeDeleteRecord: true, - onAfterDeleteRecord: false - }) + BEFORE_SET_RECORD | BEFORE_SET_FIELD | BEFORE_DELETE_RECORD );
- import { SystemHookLib } from "../src/SystemHook.sol"; + import { BEFORE_CALL_SYSTEM, AFTER_CALL_SYSTEM } from "../src/systemHookTypes.sol"; world.registerSystemHook( systemId, subscriber, - SystemHookLib.encodeBitmap({ onBeforeCallSystem: true, onAfterCallSystem: true }) + BEFORE_CALL_SYSTEM | AFTER_CALL_SYSTEM );
-
#1531
d5094a24
Thanks @alvrs! - - TheIStoreHook
interface was changed to replaceonBeforeSetField
andonAfterSetField
withonBeforeSpliceStaticData
,onAfterSpliceStaticData
,onBeforeSpliceDynamicData
andonAfterSpliceDynamicData
.This new interface matches the new
StoreSpliceStaticData
andStoreSpliceDynamicData
events, and avoids having to read the entire field from storage when only a subset of the field was updated
(e.g. when pushing elements to a field).interface IStoreHook { - function onBeforeSetField( - bytes32 tableId, - bytes32[] memory keyTuple, - uint8 fieldIndex, - bytes memory data, - FieldLayout fieldLayout - ) external; - function onAfterSetField( - bytes32 tableId, - bytes32[] memory keyTuple, - uint8 fieldIndex, - bytes memory data, - FieldLayout fieldLayout - ) external; + function onBeforeSpliceStaticData( + bytes32 tableId, + bytes32[] memory keyTuple, + uint48 start, + uint40 deleteCount, + bytes memory data + ) external; + function onAfterSpliceStaticData( + bytes32 tableId, + bytes32[] memory keyTuple, + uint48 start, + uint40 deleteCount, + bytes memory data + ) external; + function onBeforeSpliceDynamicData( + bytes32 tableId, + bytes32[] memory keyTuple, + uint8 dynamicFieldIndex, + uint40 startWithinField, + uint40 deleteCount, + bytes memory data, + PackedCounter encodedLengths + ) external; + function onAfterSpliceDynamicData( + bytes32 tableId, + bytes32[] memory keyTuple, + uint8 dynamicFieldIndex, + uint40 startWithinField, + uint40 deleteCount, + bytes memory data, + PackedCounter encodedLengths + ) external; }
-
All
calldata
parameters on theIStoreHook
interface were changed tomemory
, since the functions are called withmemory
from theWorld
. -
IStore
exposes two new functions:spliceStaticData
andspliceDynamicData
.These functions provide lower level access to the operations happening under the hood in
setField
,pushToField
,popFromField
andupdateInField
and simplify handling
the new splice hooks.StoreCore
's internal logic was simplified to use thespliceStaticData
andspliceDynamicData
functions instead of duplicating similar logic in different functions.
...
-
@latticexyz/[email protected]
Major Changes
- #1591
251170e1
Thanks @alvrs! - All optional modules have been moved from@latticexyz/world
to@latticexyz/world-modules
.
If you're using the MUD CLI, the import is already updated and no changes are necessary.
Patch Changes
-
#1592
c07fa021
Thanks @alvrs! - Tables and interfaces in theworld
package are now generated to thecodegen
folder.
This is only a breaking change if you imported tables or codegenerated interfaces from@latticexyz/world
directly.
If you're using the MUD CLI, the changed import paths are already integrated and no further changes are necessary.- import { IBaseWorld } from "@latticexyz/world/src/interfaces/IBaseWorld.sol"; + import { IBaseWorld } from "@latticexyz/world/src/codegen/interfaces/IBaseWorld.sol";
-
#1601
1890f1a0
Thanks @alvrs! - Movedstore
tables to the"store"
namespace (previously "mudstore") andworld
tables to the"world"
namespace (previously root namespace). -
Updated dependencies [
77dce993
,748f4588
,aea67c58
,07dd6f32
,c07fa021
,90e4161b
,65c9546c
,331dbfdc
,f9f9609e
,331dbfdc
,759514d8
,d5094a24
,0b8ce3f2
,de151fec
,ae340b2b
,e5d208e4
,211be2a1
,0f3e2e02
,1f80a0b5
,d0878928
,4c7fd3eb
,a0341daf
,83583a50
,5e723b90
,6573e38e
,44a5432a
,6e66c5b7
,65c9546c
,44a5432a
,672d05ca
,f1cd43bf
,31ffc9d5
,5e723b90
,63831a26
,331dbfdc
,92de5998
,5741d53d
,22ee4470
,be313068
,ac508bf1
,bfcb293d
,1890f1a0
,9b43029c
,55ab88a6
,af639a26
,5e723b90
,99ab9cd6
,c049c23f
,80dd6992
,24a6cd53
,708b49c5
,22ba7b67
,c049c23f
,251170e1
,c4f49240
,cea754dd
,95c59b20
]:- @latticexyz/[email protected]
- @latticexyz/[email protected]
- @latticexyz/[email protected]
- @latticexyz/[email protected]
- @latticexyz/[email protected]
@latticexyz/[email protected]
@latticexyz/[email protected]
@latticexyz/[email protected]
Major Changes
-
#1482
07dd6f32
Thanks @alvrs! - Renamed all occurrences ofschema
where it is used as "value schema" tovalueSchema
to clearly distinguish it from "key schema".
The only breaking change for users is the change fromschema
tovalueSchema
inmud.config.ts
.// mud.config.ts export default mudConfig({ tables: { CounterTable: { keySchema: {}, - schema: { + valueSchema: { value: "uint32", }, }, } }
-
#1354
331dbfdc
Thanks @dk1a! - We've updated Store events to be "schemaless", meaning there is enough information in each event to only need to operate on the bytes of each record to make an update to that record without having to first decode the record by its schema. This enables new kinds of indexers and sync strategies.If you've written your own sync logic or are interacting with Store calls directly, this is a breaking change. We have a few more breaking protocol changes upcoming, so you may hold off on upgrading until those land.
If you are using MUD's built-in tooling (table codegen, indexer, store sync, etc.), you don't have to make any changes except upgrading to the latest versions and deploying a fresh World.
-
The
data
field in eachStoreSetRecord
andStoreEphemeralRecord
has been replaced with three new fields:staticData
,encodedLengths
, anddynamicData
. This better reflects the on-chain state and makes it easier to perform modifications to the raw bytes. We recommend storing each of these fields individually in your off-chain storage of choice (indexer, client, etc.).- event StoreSetRecord(bytes32 tableId, bytes32[] keyTuple, bytes data); + event StoreSetRecord(bytes32 tableId, bytes32[] keyTuple, bytes staticData, bytes32 encodedLengths, bytes dynamicData); - event StoreEphemeralRecord(bytes32 tableId, bytes32[] keyTuple, bytes data); + event StoreEphemeralRecord(bytes32 tableId, bytes32[] keyTuple, bytes staticData, bytes32 encodedLengths, bytes dynamicData);
-
The
StoreSetField
event is now replaced by two new events:StoreSpliceStaticData
andStoreSpliceDynamicData
. Splicing allows us to perform efficient operations like push and pop, in addition to replacing a field value. We use two events because updating a dynamic-length field also requires updating the record'sencodedLengths
(aka PackedCounter).- event StoreSetField(bytes32 tableId, bytes32[] keyTuple, uint8 fieldIndex, bytes data); + event StoreSpliceStaticData(bytes32 tableId, bytes32[] keyTuple, uint48 start, uint40 deleteCount, bytes data); + event StoreSpliceDynamicData(bytes32 tableId, bytes32[] keyTuple, uint48 start, uint40 deleteCount, bytes data, bytes32 encodedLengths);
Similarly, Store setter methods (e.g.
setRecord
) have been updated to reflect thedata
tostaticData
,encodedLengths
, anddynamicData
changes. We'll be following up shortly with Store getter method changes for more gas efficient storage reads. -
-
#1589
f9f9609e
Thanks @alvrs! - The argument order onStore_SpliceDynamicData
,onBeforeSpliceDynamicData
andonAfterSpliceDynamicData
has been changed to match the argument order onStore_SetRecord
,
where thePackedCounter encodedLength
field comes before thebytes dynamicData
field.IStore { event Store_SpliceDynamicData( ResourceId indexed tableId, bytes32[] keyTuple, uint48 start, uint40 deleteCount, + PackedCounter encodedLengths, bytes data, - PackedCounter encodedLengths ); } IStoreHook { function onBeforeSpliceDynamicData( ResourceId tableId, bytes32[] memory keyTuple, uint8 dynamicFieldIndex, uint40 startWithinField, uint40 deleteCount, + PackedCounter encodedLengths, bytes memory data, - PackedCounter encodedLengths ) external; function onAfterSpliceDynamicData( ResourceId tableId, bytes32[] memory keyTuple, uint8 dynamicFieldIndex, uint40 startWithinField, uint40 deleteCount, + PackedCounter encodedLengths, bytes memory data, - PackedCounter encodedLengths ) external; }
-
#1527
759514d8
Thanks @holic! - Moved the registration of store hooks and systems hooks to bitmaps with bitwise operator instead of a struct.- import { StoreHookLib } from "@latticexyz/src/StoreHook.sol"; + import { + BEFORE_SET_RECORD, + BEFORE_SET_FIELD, + BEFORE_DELETE_RECORD + } from "@latticexyz/store/storeHookTypes.sol"; StoreCore.registerStoreHook( tableId, subscriber, - StoreHookLib.encodeBitmap({ - onBeforeSetRecord: true, - onAfterSetRecord: false, - onBeforeSetField: true, - onAfterSetField: false, - onBeforeDeleteRecord: true, - onAfterDeleteRecord: false - }) + BEFORE_SET_RECORD | BEFORE_SET_FIELD | BEFORE_DELETE_RECORD );
- import { SystemHookLib } from "../src/SystemHook.sol"; + import { BEFORE_CALL_SYSTEM, AFTER_CALL_SYSTEM } from "../src/systemHookTypes.sol"; world.registerSystemHook( systemId, subscriber, - SystemHookLib.encodeBitmap({ onBeforeCallSystem: true, onAfterCallSystem: true }) + BEFORE_CALL_SYSTEM | AFTER_CALL_SYSTEM );
-
#1531
d5094a24
Thanks @alvrs! - - TheIStoreHook
interface was changed to replaceonBeforeSetField
andonAfterSetField
withonBeforeSpliceStaticData
,onAfterSpliceStaticData
,onBeforeSpliceDynamicData
andonAfterSpliceDynamicData
.This new interface matches the new
StoreSpliceStaticData
andStoreSpliceDynamicData
events, and avoids having to read the entire field from storage when only a subset of the field was updated
(e.g. when pushing elements to a field).interface IStoreHook { - function onBeforeSetField( - bytes32 tableId, - bytes32[] memory keyTuple, - uint8 fieldIndex, - bytes memory data, - FieldLayout fieldLayout - ) external; - function onAfterSetField( - bytes32 tableId, - bytes32[] memory keyTuple, - uint8 fieldIndex, - bytes memory data, - FieldLayout fieldLayout - ) external; + function onBeforeSpliceStaticData( + bytes32 tableId, + bytes32[] memory keyTuple, + uint48 start, + uint40 deleteCount, + bytes memory data + ) external; + function onAfterSpliceStaticData( + bytes32 tableId, + bytes32[] memory keyTuple, + uint48 start, + uint40 deleteCount, + bytes memory data + ) external; + function onBeforeSpliceDynamicData( + bytes32 tableId, + bytes32[] memory keyTuple, + uint8 dynamicFieldIndex, + uint40 startWithinField, + uint40 deleteCount, + bytes memory data, + PackedCounter encodedLengths + ) external; + function onAfterSpliceDynamicData( + bytes32 tableId, + bytes32[] memory keyTuple, + uint8 dynamicFieldIndex, + uint40 startWithinField, + uint40 deleteCount, + bytes memory data, + PackedCounter encodedLengths + ) external; }
-
All
calldata
parameters on theIStoreHook
interface were changed tomemory
, since the functions are called withmemory
from theWorld
. -
IStore
exposes two new functions:spliceStaticData
andspliceDynamicData
.These functions provide lower level access to the operations happening under the hood in
setField
,pushToField
,popFromField
andupdateInField
and simplify handling
the new splice hooks.StoreCore
's internal logic was simplified to use thespliceStaticData
andspliceDynamicData
functions instead of duplicating similar logic in different functions.interface IStore { // Splice data in the static part of the record function spliceStaticData( bytes32 tableId, bytes32[] calldata keyTuple, uint48 start, uint40 deleteCount, bytes calldata data ) external; // Splice data in the dynamic part of the record function spliceDynamicData( bytes32 tableId, bytes32[] calldata keyTuple, uint8 dynamicFieldIndex, uint40 startWithinField, uint40 deleteCount, bytes calldata data ) external; }
-
-
#1336
de151fec
Thanks @dk1a! - - AddFieldLayout
, which is abytes32
user-type similar toSchema
.Both
FieldLayout
andSchema
have the same kind of data in the first 4 bytes.- 2 bytes for to...
@latticexyz/[email protected]
Major Changes
-
#1482
07dd6f32
Thanks @alvrs! - Renamed all occurrences ofschema
where it is used as "value schema" tovalueSchema
to clearly distinguish it from "key schema".
The only breaking change for users is the change fromschema
tovalueSchema
inmud.config.ts
.// mud.config.ts export default mudConfig({ tables: { CounterTable: { keySchema: {}, - schema: { + valueSchema: { value: "uint32", }, }, } }
-
#1354
331dbfdc
Thanks @dk1a! - We've updated Store events to be "schemaless", meaning there is enough information in each event to only need to operate on the bytes of each record to make an update to that record without having to first decode the record by its schema. This enables new kinds of indexers and sync strategies.As such, we've replaced
blockStorageOperations# @latticexyz/store-sync with
storedBlockLogs# @latticexyz/store-sync, a stream of simplified Store event logs after they've been synced to the configured storage adapter. These logs may not reflect exactly the events that are on chain when e.g. hydrating from an indexer, but they will still allow the client to "catch up" to the on-chain state of your tables.
Patch Changes
-
#1484
6573e38e
Thanks @alvrs! - Renamed all occurrences oftable
where it is used as "table ID" totableId
.
This is only a breaking change for consumers who manually decodeStore
events, but not for consumers who use the MUD libraries.event StoreSetRecord( - bytes32 table, + bytes32 tableId, bytes32[] key, bytes data ); event StoreSetField( - bytes32 table, + bytes32 tableId, bytes32[] key, uint8 fieldIndex, bytes data ); event StoreDeleteRecord( - bytes32 table, + bytes32 tableId, bytes32[] key ); event StoreEphemeralRecord( - bytes32 table, + bytes32 tableId, bytes32[] key, bytes data );
-
#1492
6e66c5b7
Thanks @alvrs! - Renamed all occurrences ofkey
where it is used as "key tuple" tokeyTuple
.
This is only a breaking change for consumers who manually decodeStore
events, but not for consumers who use the MUD libraries.event StoreSetRecord( bytes32 tableId, - bytes32[] key, + bytes32[] keyTuple, bytes data ); event StoreSetField( bytes32 tableId, - bytes32[] key, + bytes32[] keyTuple, uint8 fieldIndex, bytes data ); event StoreDeleteRecord( bytes32 tableId, - bytes32[] key, + bytes32[] keyTuple, ); event StoreEphemeralRecord( bytes32 tableId, - bytes32[] key, + bytes32[] keyTuple, bytes data );
-
#1488
7e6e5157
Thanks @holic! - Catch errors when parsing logs to tables and storage operations, log and skip -
#1586
22ee4470
Thanks @alvrs! - AllStore
andWorld
tables now use the appropriate user-types forResourceId
,FieldLayout
andSchema
to avoid manualwrap
/unwrap
. -
#1558
bfcb293d
Thanks @alvrs! - What used to be known asephemeral
table is now calledoffchain
table.
The previousephemeral
tables only supported anemitEphemeral
method, which emitted aStoreSetEphemeralRecord
event.Now
offchain
tables support all regular table methods, except partial operations on dynamic fields (push
,pop
,update
).
Unlike regular tables they don't store data on-chain but emit the same events as regular tables (StoreSetRecord
,StoreSpliceStaticData
,StoreDeleteRecord
), so their data can be indexed by offchain indexers/clients.- EphemeralTable.emitEphemeral(value); + OffchainTable.set(value);
-
#1601
1890f1a0
Thanks @alvrs! - Movedstore
tables to the"store"
namespace (previously "mudstore") andworld
tables to the"world"
namespace (previously root namespace). -
#1577
af639a26
Thanks @alvrs! -Store
events have been renamed for consistency and readability.
If you're parsingStore
events manually, you need to update your ABI.
If you're using the MUD sync stack, the new events are already integrated and no further changes are necessary.- event StoreSetRecord( + event Store_SetRecord( ResourceId indexed tableId, bytes32[] keyTuple, bytes staticData, bytes32 encodedLengths, bytes dynamicData ); - event StoreSpliceStaticData( + event Store_SpliceStaticData( ResourceId indexed tableId, bytes32[] keyTuple, uint48 start, uint40 deleteCount, bytes data ); - event StoreSpliceDynamicData( + event Store_SpliceDynamicData( ResourceId indexed tableId, bytes32[] keyTuple, uint48 start, uint40 deleteCount, bytes data, bytes32 encodedLengths ); - event StoreDeleteRecord( + event Store_DeleteRecord( ResourceId indexed tableId, bytes32[] keyTuple );
-
#1581
cea754dd
Thanks @alvrs! - - The externalsetRecord
anddeleteRecord
methods ofIStore
no longer accept aFieldLayout
as input, but load it from storage instead.
This is to prevent invalidFieldLayout
values being passed, which could cause the onchain state to diverge from the indexer state.
However, the internalStoreCore
library still exposes asetRecord
anddeleteRecord
method that allows aFieldLayout
to be passed.
This is becauseStoreCore
can only be used internally, so theFieldLayout
value can be trusted and we can save the gas for accessing storage.interface IStore { function setRecord( ResourceId tableId, bytes32[] calldata keyTuple, bytes calldata staticData, PackedCounter encodedLengths, bytes calldata dynamicData, - FieldLayout fieldLayout ) external; function deleteRecord( ResourceId tableId, bytes32[] memory keyTuple, - FieldLayout fieldLayout ) external; }
-
The
spliceStaticData
method andStore_SpliceStaticData
event ofIStore
andStoreCore
no longer includedeleteCount
in their signature.
This is because when splicing static data, the data afterstart
is always overwritten withdata
instead of being shifted, sodeleteCount
is always the length of the data to be written.event Store_SpliceStaticData( ResourceId indexed tableId, bytes32[] keyTuple, uint48 start, - uint40 deleteCount, bytes data ); interface IStore { function spliceStaticData( ResourceId tableId, bytes32[] calldata keyTuple, uint48 start, - uint40 deleteCount, bytes calldata data ) external; }
-
The
updateInField
method has been removed fromIStore
, as it's almost identical to the more generalspliceDynamicData
.
If you're manually callingupdateInField
, here is how to upgrade tospliceDynamicData
:- store.updateInField(tableId, keyTuple, fieldIndex, startByteIndex, dataToSet, fieldLayout); + uint8 dynamicFieldIndex = fieldIndex - fieldLayout.numStaticFields(); + store.spliceDynamicData(tableId, keyTuple, dynamicFieldIndex, uint40(startByteIndex), uint40(dataToSet.length), dataToSet);
-
All other methods that are only valid for dynamic fields (
pushToField
,popFromField
,getFieldSlice
)
have been renamed to make this more explicit (pushToDynamicField
,popFromDynamicField
,getDynamicFieldSlice
).Their
fieldIndex
parameter has been replaced by adynamicFieldIndex
parameter, which is the index relative to the first dynamic field (i.e.dynamicFieldIndex
=fieldIndex
-numStaticFields
).
TheFieldLayout
parameter has been removed, as it was only used to calculate thedynamicFieldIndex
in the method.interface IStore { - function pushToField( + function pushToDynamicField( ResourceId tableId, bytes32[] calldata keyTuple, - uint8 fieldInd...
-
@latticexyz/[email protected]
Minor Changes
-
#1526
498d05e3
Thanks @holic! - You can now install and run@latticexyz/store-indexer
from the npm package itself, without having to clone/build the MUD repo:npm install @latticexyz/store-indexer npm sqlite-indexer # or npm postgres-indexer
or
npx -p @latticexyz/store-indexer sqlite-indexer # or npx -p @latticexyz/store-indexer postgres-indexer
The binary will also load the nearby
.env
file for easier local configuration.We've removed the
CHAIN_ID
requirement and instead require just aRPC_HTTP_URL
orRPC_WS_URL
or both. You can now also adjust the polling interval withPOLLING_INTERVAL
(defaults to 1000ms, which corresponds to MUD's default block time).
Patch Changes
-
#1514
ed07018b
Thanks @holic! - Fixes postgres indexer stopping sync after it catches up to the latest block. -
#1546
301bcb75
Thanks @holic! - Improves error message when parsing env variables -
#1533
b3c22a18
Thanks @holic! - Added README and refactored handling of common environment variables -
Updated dependencies [
aea67c58
,07dd6f32
,90e4161b
,65c9546c
,331dbfdc
,f9f9609e
,331dbfdc
,759514d8
,d5094a24
,0b8ce3f2
,de151fec
,ae340b2b
,211be2a1
,0f3e2e02
,d0878928
,83583a50
,5e723b90
,6573e38e
,44a5432a
,6e66c5b7
,65c9546c
,44a5432a
,672d05ca
,7e6e5157
,63831a26
,331dbfdc
,92de5998
,22ee4470
,be313068
,ac508bf1
,331dbfdc
,bfcb293d
,1890f1a0
,9b43029c
,55ab88a6
,af639a26
,5e723b90
,99ab9cd6
,c049c23f
,80dd6992
,24a6cd53
,708b49c5
,22ba7b67
,c4f49240
,cea754dd
]:- @latticexyz/[email protected]
- @latticexyz/[email protected]
- @latticexyz/[email protected]
- @latticexyz/[email protected]