From c3b760f5dd4b5bddb0ff72d0014ee9ed75de7167 Mon Sep 17 00:00:00 2001 From: Kevin Ingersoll Date: Mon, 25 Sep 2023 14:23:12 +0000 Subject: [PATCH 1/8] move some events and version around --- packages/store/src/IStore.sol | 14 ++++++++------ packages/store/src/Store.sol | 21 +++++++++++++++++++++ packages/store/src/StoreCore.sol | 2 -- packages/store/src/StoreRead.sol | 4 ---- packages/store/test/StoreMock.sol | 5 +++-- 5 files changed, 32 insertions(+), 14 deletions(-) create mode 100644 packages/store/src/Store.sol diff --git a/packages/store/src/IStore.sol b/packages/store/src/IStore.sol index a7cfea1b8c..877349a625 100644 --- a/packages/store/src/IStore.sol +++ b/packages/store/src/IStore.sol @@ -9,10 +9,6 @@ import { IStoreHook } from "./IStoreHook.sol"; import { ResourceId } from "./ResourceId.sol"; interface IStoreRead { - event HelloStore(bytes32 indexed storeVersion); - - function storeVersion() external view returns (bytes32); - function getFieldLayout(ResourceId tableId) external view returns (FieldLayout fieldLayout); function getValueSchema(ResourceId tableId) external view returns (Schema valueSchema); @@ -118,7 +114,7 @@ interface IStoreRead { ) external view returns (bytes memory data); } -interface IStoreWrite { +interface IStoreEvents { event Store_SetRecord( ResourceId indexed tableId, bytes32[] keyTuple, @@ -136,7 +132,9 @@ interface IStoreWrite { bytes data ); event Store_DeleteRecord(ResourceId indexed tableId, bytes32[] keyTuple); +} +interface IStoreWrite is IStoreEvents { // Set full record (including full dynamic data) function setRecord( ResourceId tableId, @@ -242,4 +240,8 @@ interface IStoreRegistration { function unregisterStoreHook(ResourceId tableId, IStoreHook hookAddress) external; } -interface IStore is IStoreData, IStoreRegistration, IStoreErrors {} +interface IStore is IStoreData, IStoreRegistration, IStoreErrors { + event HelloStore(bytes32 indexed storeVersion); + + function storeVersion() external view returns (bytes32); +} diff --git a/packages/store/src/Store.sol b/packages/store/src/Store.sol new file mode 100644 index 0000000000..45fffb9107 --- /dev/null +++ b/packages/store/src/Store.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.21; + +import { STORE_VERSION } from "./version.sol"; +import { IStore } from "./IStore.sol"; +import { StoreRead } from "./StoreRead.sol"; +import { StoreCore } from "./StoreCore.sol"; +import { FieldLayout } from "./FieldLayout.sol"; +import { Schema } from "./Schema.sol"; +import { PackedCounter } from "./PackedCounter.sol"; +import { ResourceId } from "./ResourceId.sol"; + +abstract contract Store is IStore, StoreRead { + constructor() { + emit HelloStore(STORE_VERSION); + } + + function storeVersion() public pure returns (bytes32) { + return STORE_VERSION; + } +} diff --git a/packages/store/src/StoreCore.sol b/packages/store/src/StoreCore.sol index 22c3d17974..dcee036261 100644 --- a/packages/store/src/StoreCore.sol +++ b/packages/store/src/StoreCore.sol @@ -51,8 +51,6 @@ library StoreCore { * Consumers must call this function in their constructor. */ function initialize() internal { - emit HelloStore(STORE_VERSION); - // StoreSwitch uses the storeAddress to decide where to write data to. // If StoreSwitch is called in the context of a Store contract (storeAddress == address(this)), // StoreSwitch uses internal methods to write data instead of external calls. diff --git a/packages/store/src/StoreRead.sol b/packages/store/src/StoreRead.sol index a9720b6907..126a7852fd 100644 --- a/packages/store/src/StoreRead.sol +++ b/packages/store/src/StoreRead.sol @@ -10,10 +10,6 @@ import { PackedCounter } from "./PackedCounter.sol"; import { ResourceId } from "./ResourceId.sol"; contract StoreRead is IStoreRead { - function storeVersion() public pure returns (bytes32) { - return STORE_VERSION; - } - function getFieldLayout(ResourceId tableId) public view virtual returns (FieldLayout fieldLayout) { fieldLayout = StoreCore.getFieldLayout(tableId); } diff --git a/packages/store/test/StoreMock.sol b/packages/store/test/StoreMock.sol index 590a53f033..ad61cbb4d5 100644 --- a/packages/store/test/StoreMock.sol +++ b/packages/store/test/StoreMock.sol @@ -1,7 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.21; -import { IStore, IStoreHook } from "../src/IStore.sol"; +import { Store } from "../src/Store.sol"; +import { IStoreHook } from "../src/IStore.sol"; import { PackedCounter } from "../src/PackedCounter.sol"; import { StoreCore } from "../src/StoreCore.sol"; import { Schema } from "../src/Schema.sol"; @@ -12,7 +13,7 @@ import { ResourceId } from "../src/ResourceId.sol"; /** * StoreMock is a contract wrapper around the StoreCore library for testing purposes. */ -contract StoreMock is IStore, StoreRead { +contract StoreMock is Store { constructor() { StoreCore.initialize(); StoreCore.registerCoreTables(); From d4c23cc404bf66b38d2e8a254430682bd4713ea2 Mon Sep 17 00:00:00 2001 From: Kevin Ingersoll Date: Mon, 25 Sep 2023 14:49:36 +0000 Subject: [PATCH 2/8] base StoreData contract --- packages/store/src/IStore.sol | 8 +++----- packages/store/src/{Store.sol => StoreData.sol} | 4 ++-- packages/store/test/StoreMock.sol | 6 +++--- packages/world/src/World.sol | 7 +++---- 4 files changed, 11 insertions(+), 14 deletions(-) rename packages/store/src/{Store.sol => StoreData.sol} (83%) diff --git a/packages/store/src/IStore.sol b/packages/store/src/IStore.sol index 877349a625..db5b7c1224 100644 --- a/packages/store/src/IStore.sol +++ b/packages/store/src/IStore.sol @@ -215,7 +215,9 @@ interface IStoreWrite is IStoreEvents { * optimizing their gas cost */ interface IStoreData is IStoreRead, IStoreWrite { + event HelloStore(bytes32 indexed storeVersion); + function storeVersion() external view returns (bytes32); } /** @@ -240,8 +242,4 @@ interface IStoreRegistration { function unregisterStoreHook(ResourceId tableId, IStoreHook hookAddress) external; } -interface IStore is IStoreData, IStoreRegistration, IStoreErrors { - event HelloStore(bytes32 indexed storeVersion); - - function storeVersion() external view returns (bytes32); -} +interface IStore is IStoreData, IStoreRegistration, IStoreErrors {} diff --git a/packages/store/src/Store.sol b/packages/store/src/StoreData.sol similarity index 83% rename from packages/store/src/Store.sol rename to packages/store/src/StoreData.sol index 45fffb9107..324fc5773a 100644 --- a/packages/store/src/Store.sol +++ b/packages/store/src/StoreData.sol @@ -2,7 +2,7 @@ pragma solidity >=0.8.21; import { STORE_VERSION } from "./version.sol"; -import { IStore } from "./IStore.sol"; +import { IStore, IStoreData } from "./IStore.sol"; import { StoreRead } from "./StoreRead.sol"; import { StoreCore } from "./StoreCore.sol"; import { FieldLayout } from "./FieldLayout.sol"; @@ -10,7 +10,7 @@ import { Schema } from "./Schema.sol"; import { PackedCounter } from "./PackedCounter.sol"; import { ResourceId } from "./ResourceId.sol"; -abstract contract Store is IStore, StoreRead { +abstract contract StoreData is IStoreData, StoreRead { constructor() { emit HelloStore(STORE_VERSION); } diff --git a/packages/store/test/StoreMock.sol b/packages/store/test/StoreMock.sol index ad61cbb4d5..6ed4dd2f0f 100644 --- a/packages/store/test/StoreMock.sol +++ b/packages/store/test/StoreMock.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.21; -import { Store } from "../src/Store.sol"; -import { IStoreHook } from "../src/IStore.sol"; +import { IStore, IStoreHook } from "../src/IStore.sol"; +import { StoreData } from "../src/StoreData.sol"; import { PackedCounter } from "../src/PackedCounter.sol"; import { StoreCore } from "../src/StoreCore.sol"; import { Schema } from "../src/Schema.sol"; @@ -13,7 +13,7 @@ import { ResourceId } from "../src/ResourceId.sol"; /** * StoreMock is a contract wrapper around the StoreCore library for testing purposes. */ -contract StoreMock is Store { +contract StoreMock is IStore, StoreData { constructor() { StoreCore.initialize(); StoreCore.registerCoreTables(); diff --git a/packages/world/src/World.sol b/packages/world/src/World.sol index 838ba41e1f..46958da260 100644 --- a/packages/world/src/World.sol +++ b/packages/world/src/World.sol @@ -1,9 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.21; -import { StoreRead } from "@latticexyz/store/src/StoreRead.sol"; -import { StoreCore } from "@latticexyz/store/src/StoreCore.sol"; -import { IStoreData } from "@latticexyz/store/src/IStore.sol"; +import { StoreData } from "@latticexyz/store/src/StoreData.sol"; +import { IStore } from "@latticexyz/store/src/IStore.sol"; import { StoreCore } from "@latticexyz/store/src/StoreCore.sol"; import { Bytes } from "@latticexyz/store/src/Bytes.sol"; import { Schema } from "@latticexyz/store/src/Schema.sol"; @@ -36,7 +35,7 @@ import { FunctionSelectors } from "./codegen/tables/FunctionSelectors.sol"; import { Balances } from "./codegen/tables/Balances.sol"; import { CORE_MODULE_NAME } from "./modules/core/constants.sol"; -contract World is StoreRead, IStoreData, IWorldKernel { +contract World is StoreData, IWorldKernel { using WorldResourceIdInstance for ResourceId; address public immutable creator; From a2bca5748f3cee08c65c2d9a769cf8d1b15900ca Mon Sep 17 00:00:00 2001 From: Kevin Ingersoll Date: Mon, 25 Sep 2023 14:50:55 +0000 Subject: [PATCH 3/8] remove unused imports --- packages/store/src/StoreData.sol | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/store/src/StoreData.sol b/packages/store/src/StoreData.sol index 324fc5773a..4fb40652bf 100644 --- a/packages/store/src/StoreData.sol +++ b/packages/store/src/StoreData.sol @@ -2,13 +2,8 @@ pragma solidity >=0.8.21; import { STORE_VERSION } from "./version.sol"; -import { IStore, IStoreData } from "./IStore.sol"; +import { IStoreData } from "./IStore.sol"; import { StoreRead } from "./StoreRead.sol"; -import { StoreCore } from "./StoreCore.sol"; -import { FieldLayout } from "./FieldLayout.sol"; -import { Schema } from "./Schema.sol"; -import { PackedCounter } from "./PackedCounter.sol"; -import { ResourceId } from "./ResourceId.sol"; abstract contract StoreData is IStoreData, StoreRead { constructor() { From 33a2ab105c4cb12f206a4f17354c99ddc7fb909c Mon Sep 17 00:00:00 2001 From: Kevin Ingersoll Date: Mon, 25 Sep 2023 14:55:20 +0000 Subject: [PATCH 4/8] move initializer --- packages/store/src/StoreData.sol | 2 ++ packages/world/src/World.sol | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/store/src/StoreData.sol b/packages/store/src/StoreData.sol index 4fb40652bf..75fd23f1e1 100644 --- a/packages/store/src/StoreData.sol +++ b/packages/store/src/StoreData.sol @@ -4,9 +4,11 @@ pragma solidity >=0.8.21; import { STORE_VERSION } from "./version.sol"; import { IStoreData } from "./IStore.sol"; import { StoreRead } from "./StoreRead.sol"; +import { StoreCore } from "./StoreCore.sol"; abstract contract StoreData is IStoreData, StoreRead { constructor() { + StoreCore.initialize(); emit HelloStore(STORE_VERSION); } diff --git a/packages/world/src/World.sol b/packages/world/src/World.sol index 46958da260..9c7fd2277e 100644 --- a/packages/world/src/World.sol +++ b/packages/world/src/World.sol @@ -46,7 +46,6 @@ contract World is StoreData, IWorldKernel { constructor() { creator = msg.sender; - StoreCore.initialize(); emit HelloWorld(WORLD_VERSION); } From 7742e86d056d80cdd8b3f0c90bc28ff8a69020f1 Mon Sep 17 00:00:00 2001 From: Kevin Ingersoll Date: Mon, 25 Sep 2023 15:04:35 +0000 Subject: [PATCH 5/8] move events to its own file for visibility --- packages/store/src/IStore.sol | 21 +-------------------- packages/store/src/IStoreEvents.sol | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 20 deletions(-) create mode 100644 packages/store/src/IStoreEvents.sol diff --git a/packages/store/src/IStore.sol b/packages/store/src/IStore.sol index db5b7c1224..f76a83621e 100644 --- a/packages/store/src/IStore.sol +++ b/packages/store/src/IStore.sol @@ -2,6 +2,7 @@ pragma solidity >=0.8.21; import { IStoreErrors } from "./IStoreErrors.sol"; +import { IStoreEvents } from "./IStoreEvents.sol"; import { PackedCounter } from "./PackedCounter.sol"; import { FieldLayout } from "./FieldLayout.sol"; import { Schema } from "./Schema.sol"; @@ -114,26 +115,6 @@ interface IStoreRead { ) external view returns (bytes memory data); } -interface IStoreEvents { - event Store_SetRecord( - ResourceId indexed tableId, - bytes32[] keyTuple, - bytes staticData, - PackedCounter encodedLengths, - bytes dynamicData - ); - event Store_SpliceStaticData(ResourceId indexed tableId, bytes32[] keyTuple, uint48 start, bytes data); - event Store_SpliceDynamicData( - ResourceId indexed tableId, - bytes32[] keyTuple, - uint48 start, - uint40 deleteCount, - PackedCounter encodedLengths, - bytes data - ); - event Store_DeleteRecord(ResourceId indexed tableId, bytes32[] keyTuple); -} - interface IStoreWrite is IStoreEvents { // Set full record (including full dynamic data) function setRecord( diff --git a/packages/store/src/IStoreEvents.sol b/packages/store/src/IStoreEvents.sol new file mode 100644 index 0000000000..6ae89c1304 --- /dev/null +++ b/packages/store/src/IStoreEvents.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.21; + +import { ResourceId } from "./ResourceId.sol"; + +interface IStoreEvents { + event Store_SetRecord( + ResourceId indexed tableId, + bytes32[] keyTuple, + bytes staticData, + PackedCounter encodedLengths, + bytes dynamicData + ); + event Store_SpliceStaticData(ResourceId indexed tableId, bytes32[] keyTuple, uint48 start, bytes data); + event Store_SpliceDynamicData( + ResourceId indexed tableId, + bytes32[] keyTuple, + uint48 start, + uint40 deleteCount, + PackedCounter encodedLengths, + bytes data + ); + event Store_DeleteRecord(ResourceId indexed tableId, bytes32[] keyTuple); +} From c349d8158602d2bdd99f5cb8709008dc08c966d0 Mon Sep 17 00:00:00 2001 From: Kevin Ingersoll Date: Mon, 25 Sep 2023 15:10:48 +0000 Subject: [PATCH 6/8] move interfaces to their own files --- packages/store/src/IStore.sol | 222 +--------------------- packages/store/src/IStoreData.sol | 16 ++ packages/store/src/IStoreRead.sol | 113 +++++++++++ packages/store/src/IStoreRegistration.sol | 29 +++ packages/store/src/IStoreWrite.sol | 82 ++++++++ 5 files changed, 242 insertions(+), 220 deletions(-) create mode 100644 packages/store/src/IStoreData.sol create mode 100644 packages/store/src/IStoreRead.sol create mode 100644 packages/store/src/IStoreRegistration.sol create mode 100644 packages/store/src/IStoreWrite.sol diff --git a/packages/store/src/IStore.sol b/packages/store/src/IStore.sol index f76a83621e..1799e17460 100644 --- a/packages/store/src/IStore.sol +++ b/packages/store/src/IStore.sol @@ -2,225 +2,7 @@ pragma solidity >=0.8.21; import { IStoreErrors } from "./IStoreErrors.sol"; -import { IStoreEvents } from "./IStoreEvents.sol"; -import { PackedCounter } from "./PackedCounter.sol"; -import { FieldLayout } from "./FieldLayout.sol"; -import { Schema } from "./Schema.sol"; -import { IStoreHook } from "./IStoreHook.sol"; -import { ResourceId } from "./ResourceId.sol"; - -interface IStoreRead { - function getFieldLayout(ResourceId tableId) external view returns (FieldLayout fieldLayout); - - function getValueSchema(ResourceId tableId) external view returns (Schema valueSchema); - - function getKeySchema(ResourceId tableId) external view returns (Schema keySchema); - - /** - * Get full record (all fields, static and dynamic data) for the given tableId and key tuple, loading the field layout from storage - */ - function getRecord( - ResourceId tableId, - bytes32[] calldata keyTuple - ) external view returns (bytes memory staticData, PackedCounter encodedLengths, bytes memory dynamicData); - - /** - * Get full record (all fields, static and dynamic data) for the given tableId and key tuple, with the given field layout - */ - function getRecord( - ResourceId tableId, - bytes32[] calldata keyTuple, - FieldLayout fieldLayout - ) external view returns (bytes memory staticData, PackedCounter encodedLengths, bytes memory dynamicData); - - /** - * Get a single field from the given tableId and key tuple, loading the field layout from storage - */ - function getField( - ResourceId tableId, - bytes32[] calldata keyTuple, - uint8 fieldIndex - ) external view returns (bytes memory data); - - /** - * Get a single field from the given tableId and key tuple, with the given field layout - */ - function getField( - ResourceId tableId, - bytes32[] calldata keyTuple, - uint8 fieldIndex, - FieldLayout fieldLayout - ) external view returns (bytes memory data); - - /** - * Get a single static field from the given tableId and key tuple, with the given value field layout. - * Note: the field value is left-aligned in the returned bytes32, the rest of the word is not zeroed out. - * Consumers are expected to truncate the returned value as needed. - */ - function getStaticField( - ResourceId tableId, - bytes32[] calldata keyTuple, - uint8 fieldIndex, - FieldLayout fieldLayout - ) external view returns (bytes32); - - /** - * Get a single dynamic field from the given tableId and key tuple at the given dynamic field index. - * (Dynamic field index = field index - number of static fields) - */ - function getDynamicField( - ResourceId tableId, - bytes32[] memory keyTuple, - uint8 dynamicFieldIndex - ) external view returns (bytes memory); - - /** - * Get the byte length of a single field from the given tableId and key tuple, loading the field layout from storage - */ - function getFieldLength( - ResourceId tableId, - bytes32[] memory keyTuple, - uint8 fieldIndex - ) external view returns (uint256); - - /** - * Get the byte length of a single field from the given tableId and key tuple, with the given value field layout - */ - function getFieldLength( - ResourceId tableId, - bytes32[] memory keyTuple, - uint8 fieldIndex, - FieldLayout fieldLayout - ) external view returns (uint256); - - /** - * Get the byte length of a single dynamic field from the given tableId and key tuple - */ - function getDynamicFieldLength( - ResourceId tableId, - bytes32[] memory keyTuple, - uint8 dynamicFieldIndex - ) external view returns (uint256); - - /** - * Get a byte slice (including start, excluding end) of a single dynamic field from the given tableId and key tuple, with the given value field layout. - * The slice is unchecked and will return invalid data if `start`:`end` overflow. - */ - function getDynamicFieldSlice( - ResourceId tableId, - bytes32[] memory keyTuple, - uint8 dynamicFieldIndex, - uint256 start, - uint256 end - ) external view returns (bytes memory data); -} - -interface IStoreWrite is IStoreEvents { - // Set full record (including full dynamic data) - function setRecord( - ResourceId tableId, - bytes32[] calldata keyTuple, - bytes calldata staticData, - PackedCounter encodedLengths, - bytes calldata dynamicData - ) external; - - // Splice data in the static part of the record - function spliceStaticData( - ResourceId tableId, - bytes32[] calldata keyTuple, - uint48 start, - bytes calldata data - ) external; - - // Splice data in the dynamic part of the record - function spliceDynamicData( - ResourceId tableId, - bytes32[] calldata keyTuple, - uint8 dynamicFieldIndex, - uint40 startWithinField, - uint40 deleteCount, - bytes calldata data - ) external; - - // Set partial data at field index - function setField(ResourceId tableId, bytes32[] calldata keyTuple, uint8 fieldIndex, bytes calldata data) external; - - // Set partial data at field index - function setField( - ResourceId tableId, - bytes32[] calldata keyTuple, - uint8 fieldIndex, - bytes calldata data, - FieldLayout fieldLayout - ) external; - - function setStaticField( - ResourceId tableId, - bytes32[] calldata keyTuple, - uint8 fieldIndex, - bytes calldata data, - FieldLayout fieldLayout - ) external; - - function setDynamicField( - ResourceId tableId, - bytes32[] calldata keyTuple, - uint8 dynamicFieldIndex, - bytes calldata data - ) external; - - // Push encoded items to the dynamic field at field index - function pushToDynamicField( - ResourceId tableId, - bytes32[] calldata keyTuple, - uint8 dynamicFieldIndex, - bytes calldata dataToPush - ) external; - - // Pop byte length from the dynamic field at field index - function popFromDynamicField( - ResourceId tableId, - bytes32[] calldata keyTuple, - uint8 dynamicFieldIndex, - uint256 byteLengthToPop - ) external; - - // Set full record (including full dynamic data) - function deleteRecord(ResourceId tableId, bytes32[] memory keyTuple) external; -} - -/** - * The IStoreData interface includes methods for reading and writing table values. - * These methods are frequently invoked during runtime, so it is essential to prioritize - * optimizing their gas cost - */ -interface IStoreData is IStoreRead, IStoreWrite { - event HelloStore(bytes32 indexed storeVersion); - - function storeVersion() external view returns (bytes32); -} - -/** - * The IStoreRegistration interface includes methods for managing table field layouts, - * metadata, and hooks, which are usually called once in the setup phase of an application, - * making them less performance critical than the IStoreData methods. - */ -interface IStoreRegistration { - function registerTable( - ResourceId tableId, - FieldLayout fieldLayout, - Schema keySchema, - Schema valueSchema, - string[] calldata keyNames, - string[] calldata fieldNames - ) external; - - // Register hook to be called when a record or field is set or deleted - function registerStoreHook(ResourceId tableId, IStoreHook hookAddress, uint8 enabledHooksBitmap) external; - - // Unregister a hook for the given tableId - function unregisterStoreHook(ResourceId tableId, IStoreHook hookAddress) external; -} +import { IStoreData } from "./IStoreData.sol"; +import { IStoreRegistration } from "./IStoreRegistration.sol"; interface IStore is IStoreData, IStoreRegistration, IStoreErrors {} diff --git a/packages/store/src/IStoreData.sol b/packages/store/src/IStoreData.sol new file mode 100644 index 0000000000..31b66a3999 --- /dev/null +++ b/packages/store/src/IStoreData.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.21; + +import { IStoreRead } from "./IStoreRead.sol"; +import { IStoreWrite } from "./IStoreWrite.sol"; + +/** + * The IStoreData interface includes methods for reading and writing table values. + * These methods are frequently invoked during runtime, so it is essential to prioritize + * optimizing their gas cost + */ +interface IStoreData is IStoreRead, IStoreWrite { + event HelloStore(bytes32 indexed storeVersion); + + function storeVersion() external view returns (bytes32); +} diff --git a/packages/store/src/IStoreRead.sol b/packages/store/src/IStoreRead.sol new file mode 100644 index 0000000000..794eb8280f --- /dev/null +++ b/packages/store/src/IStoreRead.sol @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.21; + +import { PackedCounter } from "./PackedCounter.sol"; +import { FieldLayout } from "./FieldLayout.sol"; +import { Schema } from "./Schema.sol"; +import { ResourceId } from "./ResourceId.sol"; + +interface IStoreRead { + function getFieldLayout(ResourceId tableId) external view returns (FieldLayout fieldLayout); + + function getValueSchema(ResourceId tableId) external view returns (Schema valueSchema); + + function getKeySchema(ResourceId tableId) external view returns (Schema keySchema); + + /** + * Get full record (all fields, static and dynamic data) for the given tableId and key tuple, loading the field layout from storage + */ + function getRecord( + ResourceId tableId, + bytes32[] calldata keyTuple + ) external view returns (bytes memory staticData, PackedCounter encodedLengths, bytes memory dynamicData); + + /** + * Get full record (all fields, static and dynamic data) for the given tableId and key tuple, with the given field layout + */ + function getRecord( + ResourceId tableId, + bytes32[] calldata keyTuple, + FieldLayout fieldLayout + ) external view returns (bytes memory staticData, PackedCounter encodedLengths, bytes memory dynamicData); + + /** + * Get a single field from the given tableId and key tuple, loading the field layout from storage + */ + function getField( + ResourceId tableId, + bytes32[] calldata keyTuple, + uint8 fieldIndex + ) external view returns (bytes memory data); + + /** + * Get a single field from the given tableId and key tuple, with the given field layout + */ + function getField( + ResourceId tableId, + bytes32[] calldata keyTuple, + uint8 fieldIndex, + FieldLayout fieldLayout + ) external view returns (bytes memory data); + + /** + * Get a single static field from the given tableId and key tuple, with the given value field layout. + * Note: the field value is left-aligned in the returned bytes32, the rest of the word is not zeroed out. + * Consumers are expected to truncate the returned value as needed. + */ + function getStaticField( + ResourceId tableId, + bytes32[] calldata keyTuple, + uint8 fieldIndex, + FieldLayout fieldLayout + ) external view returns (bytes32); + + /** + * Get a single dynamic field from the given tableId and key tuple at the given dynamic field index. + * (Dynamic field index = field index - number of static fields) + */ + function getDynamicField( + ResourceId tableId, + bytes32[] memory keyTuple, + uint8 dynamicFieldIndex + ) external view returns (bytes memory); + + /** + * Get the byte length of a single field from the given tableId and key tuple, loading the field layout from storage + */ + function getFieldLength( + ResourceId tableId, + bytes32[] memory keyTuple, + uint8 fieldIndex + ) external view returns (uint256); + + /** + * Get the byte length of a single field from the given tableId and key tuple, with the given value field layout + */ + function getFieldLength( + ResourceId tableId, + bytes32[] memory keyTuple, + uint8 fieldIndex, + FieldLayout fieldLayout + ) external view returns (uint256); + + /** + * Get the byte length of a single dynamic field from the given tableId and key tuple + */ + function getDynamicFieldLength( + ResourceId tableId, + bytes32[] memory keyTuple, + uint8 dynamicFieldIndex + ) external view returns (uint256); + + /** + * Get a byte slice (including start, excluding end) of a single dynamic field from the given tableId and key tuple, with the given value field layout. + * The slice is unchecked and will return invalid data if `start`:`end` overflow. + */ + function getDynamicFieldSlice( + ResourceId tableId, + bytes32[] memory keyTuple, + uint8 dynamicFieldIndex, + uint256 start, + uint256 end + ) external view returns (bytes memory data); +} diff --git a/packages/store/src/IStoreRegistration.sol b/packages/store/src/IStoreRegistration.sol new file mode 100644 index 0000000000..58d73ab910 --- /dev/null +++ b/packages/store/src/IStoreRegistration.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.21; + +import { FieldLayout } from "./FieldLayout.sol"; +import { Schema } from "./Schema.sol"; +import { IStoreHook } from "./IStoreHook.sol"; +import { ResourceId } from "./ResourceId.sol"; + +/** + * The IStoreRegistration interface includes methods for managing table field layouts, + * metadata, and hooks, which are usually called once in the setup phase of an application, + * making them less performance critical than the methods. + */ +interface IStoreRegistration { + function registerTable( + ResourceId tableId, + FieldLayout fieldLayout, + Schema keySchema, + Schema valueSchema, + string[] calldata keyNames, + string[] calldata fieldNames + ) external; + + // Register hook to be called when a record or field is set or deleted + function registerStoreHook(ResourceId tableId, IStoreHook hookAddress, uint8 enabledHooksBitmap) external; + + // Unregister a hook for the given tableId + function unregisterStoreHook(ResourceId tableId, IStoreHook hookAddress) external; +} diff --git a/packages/store/src/IStoreWrite.sol b/packages/store/src/IStoreWrite.sol new file mode 100644 index 0000000000..99f6e3048c --- /dev/null +++ b/packages/store/src/IStoreWrite.sol @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.21; + +import { IStoreEvents } from "./IStoreEvents.sol"; +import { PackedCounter } from "./PackedCounter.sol"; +import { FieldLayout } from "./FieldLayout.sol"; +import { ResourceId } from "./ResourceId.sol"; + +interface IStoreWrite is IStoreEvents { + // Set full record (including full dynamic data) + function setRecord( + ResourceId tableId, + bytes32[] calldata keyTuple, + bytes calldata staticData, + PackedCounter encodedLengths, + bytes calldata dynamicData + ) external; + + // Splice data in the static part of the record + function spliceStaticData( + ResourceId tableId, + bytes32[] calldata keyTuple, + uint48 start, + bytes calldata data + ) external; + + // Splice data in the dynamic part of the record + function spliceDynamicData( + ResourceId tableId, + bytes32[] calldata keyTuple, + uint8 dynamicFieldIndex, + uint40 startWithinField, + uint40 deleteCount, + bytes calldata data + ) external; + + // Set partial data at field index + function setField(ResourceId tableId, bytes32[] calldata keyTuple, uint8 fieldIndex, bytes calldata data) external; + + // Set partial data at field index + function setField( + ResourceId tableId, + bytes32[] calldata keyTuple, + uint8 fieldIndex, + bytes calldata data, + FieldLayout fieldLayout + ) external; + + function setStaticField( + ResourceId tableId, + bytes32[] calldata keyTuple, + uint8 fieldIndex, + bytes calldata data, + FieldLayout fieldLayout + ) external; + + function setDynamicField( + ResourceId tableId, + bytes32[] calldata keyTuple, + uint8 dynamicFieldIndex, + bytes calldata data + ) external; + + // Push encoded items to the dynamic field at field index + function pushToDynamicField( + ResourceId tableId, + bytes32[] calldata keyTuple, + uint8 dynamicFieldIndex, + bytes calldata dataToPush + ) external; + + // Pop byte length from the dynamic field at field index + function popFromDynamicField( + ResourceId tableId, + bytes32[] calldata keyTuple, + uint8 dynamicFieldIndex, + uint256 byteLengthToPop + ) external; + + // Set full record (including full dynamic data) + function deleteRecord(ResourceId tableId, bytes32[] memory keyTuple) external; +} From d780e4f00cdf8e2455e28e92671e77d67f2878d9 Mon Sep 17 00:00:00 2001 From: Kevin Ingersoll Date: Mon, 25 Sep 2023 08:13:53 -0700 Subject: [PATCH 7/8] Create quick-years-juggle.md --- .changeset/quick-years-juggle.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .changeset/quick-years-juggle.md diff --git a/.changeset/quick-years-juggle.md b/.changeset/quick-years-juggle.md new file mode 100644 index 0000000000..6415e1c24b --- /dev/null +++ b/.changeset/quick-years-juggle.md @@ -0,0 +1,10 @@ +--- +"@latticexyz/store": major +"@latticexyz/world": minor +--- + +- Moves Store events into its own `IStoreEvents` interface +- Moves Store interfaces to their own files +- Adds a `StoreData` abstract contract to initialize a Store and expose the Store version + +If you're using MUD out of the box, you won't have to make any changes. You will only need to update if you're using any of the base Store interfaces. From 610d37a6ce620f6e679a1a2ebfd711d3a35ebc12 Mon Sep 17 00:00:00 2001 From: Kevin Ingersoll Date: Mon, 25 Sep 2023 15:18:14 +0000 Subject: [PATCH 8/8] update imports --- packages/store/src/IStoreEvents.sol | 1 + packages/store/src/StoreData.sol | 2 +- packages/store/src/StoreRead.sol | 2 +- packages/store/test/StoreHook.t.sol | 2 +- packages/store/test/StoreMock.sol | 3 ++- packages/world/test/World.t.sol | 2 +- packages/world/test/WorldDynamicUpdate.t.sol | 2 +- 7 files changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/store/src/IStoreEvents.sol b/packages/store/src/IStoreEvents.sol index 6ae89c1304..ff372e0eef 100644 --- a/packages/store/src/IStoreEvents.sol +++ b/packages/store/src/IStoreEvents.sol @@ -2,6 +2,7 @@ pragma solidity >=0.8.21; import { ResourceId } from "./ResourceId.sol"; +import { PackedCounter } from "./PackedCounter.sol"; interface IStoreEvents { event Store_SetRecord( diff --git a/packages/store/src/StoreData.sol b/packages/store/src/StoreData.sol index 75fd23f1e1..b5b99f0616 100644 --- a/packages/store/src/StoreData.sol +++ b/packages/store/src/StoreData.sol @@ -2,7 +2,7 @@ pragma solidity >=0.8.21; import { STORE_VERSION } from "./version.sol"; -import { IStoreData } from "./IStore.sol"; +import { IStoreData } from "./IStoreData.sol"; import { StoreRead } from "./StoreRead.sol"; import { StoreCore } from "./StoreCore.sol"; diff --git a/packages/store/src/StoreRead.sol b/packages/store/src/StoreRead.sol index 126a7852fd..f64d66b803 100644 --- a/packages/store/src/StoreRead.sol +++ b/packages/store/src/StoreRead.sol @@ -2,7 +2,7 @@ pragma solidity >=0.8.21; import { STORE_VERSION } from "./version.sol"; -import { IStoreRead } from "./IStore.sol"; +import { IStoreRead } from "./IStoreRead.sol"; import { StoreCore } from "./StoreCore.sol"; import { FieldLayout } from "./FieldLayout.sol"; import { Schema } from "./Schema.sol"; diff --git a/packages/store/test/StoreHook.t.sol b/packages/store/test/StoreHook.t.sol index f4d03a7471..889c8c5bfa 100644 --- a/packages/store/test/StoreHook.t.sol +++ b/packages/store/test/StoreHook.t.sol @@ -8,7 +8,7 @@ import { EchoSubscriber } from "./EchoSubscriber.sol"; import { RevertSubscriber } from "./RevertSubscriber.sol"; import { Hook, HookLib } from "../src/Hook.sol"; -import { IStoreHook } from "../src/IStore.sol"; +import { IStoreHook } from "../src/IStoreHook.sol"; import { PackedCounter } from "../src/PackedCounter.sol"; import { FieldLayout } from "../src/FieldLayout.sol"; import { ResourceId, ResourceIdLib } from "../src/ResourceId.sol"; diff --git a/packages/store/test/StoreMock.sol b/packages/store/test/StoreMock.sol index 6ed4dd2f0f..f8427117f8 100644 --- a/packages/store/test/StoreMock.sol +++ b/packages/store/test/StoreMock.sol @@ -1,7 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.21; -import { IStore, IStoreHook } from "../src/IStore.sol"; +import { IStore } from "../src/IStore.sol"; +import { IStoreHook } from "../src/IStoreHook.sol"; import { StoreData } from "../src/StoreData.sol"; import { PackedCounter } from "../src/PackedCounter.sol"; import { StoreCore } from "../src/StoreCore.sol"; diff --git a/packages/world/test/World.t.sol b/packages/world/test/World.t.sol index 70c34d2fff..d6331b04fe 100644 --- a/packages/world/test/World.t.sol +++ b/packages/world/test/World.t.sol @@ -8,7 +8,7 @@ import { SchemaType } from "@latticexyz/schema-type/src/solidity/SchemaType.sol" import { IStoreHook, STORE_HOOK_INTERFACE_ID } from "@latticexyz/store/src/IStoreHook.sol"; import { StoreCore, StoreCoreInternal } from "@latticexyz/store/src/StoreCore.sol"; -import { IStoreErrors } from "@latticexyz/store/src/IStore.sol"; +import { IStoreErrors } from "@latticexyz/store/src/IStoreErrors.sol"; import { StoreSwitch } from "@latticexyz/store/src/StoreSwitch.sol"; import { FieldLayout, FieldLayoutLib } from "@latticexyz/store/src/FieldLayout.sol"; import { FieldLayoutEncodeHelper } from "@latticexyz/store/test/FieldLayoutEncodeHelper.sol"; diff --git a/packages/world/test/WorldDynamicUpdate.t.sol b/packages/world/test/WorldDynamicUpdate.t.sol index 5d7f930481..1427669b85 100644 --- a/packages/world/test/WorldDynamicUpdate.t.sol +++ b/packages/world/test/WorldDynamicUpdate.t.sol @@ -6,7 +6,7 @@ import { GasReporter } from "@latticexyz/gas-report/src/GasReporter.sol"; import { SchemaType } from "@latticexyz/schema-type/src/solidity/SchemaType.sol"; -import { IStoreHook } from "@latticexyz/store/src/IStore.sol"; +import { IStoreHook } from "@latticexyz/store/src/IStoreHook.sol"; import { StoreCore } from "@latticexyz/store/src/StoreCore.sol"; import { StoreSwitch } from "@latticexyz/store/src/StoreSwitch.sol"; import { FieldLayout } from "@latticexyz/store/src/FieldLayout.sol";