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 inference of whether the field is static or dynamic. -
The
getDynamicFieldSlice
method no longer accepts reading outside the bounds of the dynamic field.
This is to avoid returning invalid data, as the data of a dynamic field is not deleted when the record is deleted, but only its length is set to zero.
-