diff --git a/.changeset/neat-tools-check.md b/.changeset/neat-tools-check.md new file mode 100644 index 0000000000..f8d4fa1924 --- /dev/null +++ b/.changeset/neat-tools-check.md @@ -0,0 +1,5 @@ +--- +"@latticexyz/world": patch +--- + +Added a table to track the `CoreModule` address the world was initialised with. diff --git a/packages/world-modules/gas-report.json b/packages/world-modules/gas-report.json index afb2a6d2dc..d951032107 100644 --- a/packages/world-modules/gas-report.json +++ b/packages/world-modules/gas-report.json @@ -3,139 +3,139 @@ "file": "test/ERC20.t.sol", "test": "testApprove", "name": "approve", - "gasUsed": 114354 + "gasUsed": 114348 }, { "file": "test/ERC20.t.sol", "test": "testBurn", "name": "burn", - "gasUsed": 75922 + "gasUsed": 75910 }, { "file": "test/ERC20.t.sol", "test": "testMint", "name": "mint", - "gasUsed": 161761 + "gasUsed": 161749 }, { "file": "test/ERC20.t.sol", "test": "testTransfer", "name": "transfer", - "gasUsed": 93007 + "gasUsed": 92995 }, { "file": "test/ERC20.t.sol", "test": "testTransferFrom", "name": "transferFrom", - "gasUsed": 130349 + "gasUsed": 130331 }, { "file": "test/ERC721.t.sol", "test": "testApproveAllGas", "name": "setApprovalForAll", - "gasUsed": 113978 + "gasUsed": 113972 }, { "file": "test/ERC721.t.sol", "test": "testApproveGas", "name": "approve", - "gasUsed": 87987 + "gasUsed": 87981 }, { "file": "test/ERC721.t.sol", "test": "testBurnGas", "name": "burn", - "gasUsed": 101928 + "gasUsed": 101910 }, { "file": "test/ERC721.t.sol", "test": "testMintGas", "name": "mint", - "gasUsed": 169492 + "gasUsed": 169480 }, { "file": "test/ERC721.t.sol", "test": "testSafeMintToEOAGas", "name": "safeMint", - "gasUsed": 169763 + "gasUsed": 169751 }, { "file": "test/ERC721.t.sol", "test": "testSafeTransferFromToEOAGas", "name": "safeTransferFrom", - "gasUsed": 143768 + "gasUsed": 143744 }, { "file": "test/ERC721.t.sol", "test": "testTransferFromGas", "name": "transferFrom", - "gasUsed": 136925 + "gasUsed": 136901 }, { "file": "test/KeysInTableModule.t.sol", "test": "testInstallComposite", "name": "install keys in table module", - "gasUsed": 1438518 + "gasUsed": 1438512 }, { "file": "test/KeysInTableModule.t.sol", "test": "testInstallGas", "name": "install keys in table module", - "gasUsed": 1438518 + "gasUsed": 1438512 }, { "file": "test/KeysInTableModule.t.sol", "test": "testInstallGas", "name": "set a record on a table with keysInTableModule installed", - "gasUsed": 158860 + "gasUsed": 158830 }, { "file": "test/KeysInTableModule.t.sol", "test": "testInstallSingleton", "name": "install keys in table module", - "gasUsed": 1438518 + "gasUsed": 1438512 }, { "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookCompositeGas", "name": "install keys in table module", - "gasUsed": 1438518 + "gasUsed": 1438512 }, { "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookCompositeGas", "name": "change a composite record on a table with keysInTableModule installed", - "gasUsed": 22489 + "gasUsed": 22483 }, { "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookCompositeGas", "name": "delete a composite record on a table with keysInTableModule installed", - "gasUsed": 156040 + "gasUsed": 155938 }, { "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookGas", "name": "install keys in table module", - "gasUsed": 1438518 + "gasUsed": 1438512 }, { "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookGas", "name": "change a record on a table with keysInTableModule installed", - "gasUsed": 21211 + "gasUsed": 21205 }, { "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookGas", "name": "delete a record on a table with keysInTableModule installed", - "gasUsed": 85214 + "gasUsed": 85160 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testGetKeysWithValueGas", "name": "install keys with value module", - "gasUsed": 683905 + "gasUsed": 683899 }, { "file": "test/KeysWithValueModule.t.sol", @@ -153,79 +153,79 @@ "file": "test/KeysWithValueModule.t.sol", "test": "testInstall", "name": "install keys with value module", - "gasUsed": 683905 + "gasUsed": 683899 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testInstall", "name": "set a record on a table with KeysWithValueModule installed", - "gasUsed": 135440 + "gasUsed": 135410 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testSetAndDeleteRecordHook", "name": "install keys with value module", - "gasUsed": 683905 + "gasUsed": 683899 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testSetAndDeleteRecordHook", "name": "change a record on a table with KeysWithValueModule installed", - "gasUsed": 103843 + "gasUsed": 103813 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testSetAndDeleteRecordHook", "name": "delete a record on a table with KeysWithValueModule installed", - "gasUsed": 36486 + "gasUsed": 36468 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testSetField", "name": "install keys with value module", - "gasUsed": 683905 + "gasUsed": 683899 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testSetField", "name": "set a field on a table with KeysWithValueModule installed", - "gasUsed": 146675 + "gasUsed": 146645 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testSetField", "name": "change a field on a table with KeysWithValueModule installed", - "gasUsed": 111434 + "gasUsed": 111404 }, { "file": "test/query.t.sol", "test": "testCombinedHasHasValueNotQuery", "name": "CombinedHasHasValueNotQuery", - "gasUsed": 104518 + "gasUsed": 104500 }, { "file": "test/query.t.sol", "test": "testCombinedHasHasValueQuery", "name": "CombinedHasHasValueQuery", - "gasUsed": 53216 + "gasUsed": 53210 }, { "file": "test/query.t.sol", "test": "testCombinedHasNotQuery", "name": "CombinedHasNotQuery", - "gasUsed": 130764 + "gasUsed": 130734 }, { "file": "test/query.t.sol", "test": "testCombinedHasQuery", "name": "CombinedHasQuery", - "gasUsed": 84161 + "gasUsed": 84137 }, { "file": "test/query.t.sol", "test": "testCombinedHasValueNotQuery", "name": "CombinedHasValueNotQuery", - "gasUsed": 84691 + "gasUsed": 84673 }, { "file": "test/query.t.sol", @@ -237,19 +237,19 @@ "file": "test/query.t.sol", "test": "testHasQuery", "name": "HasQuery", - "gasUsed": 18808 + "gasUsed": 18802 }, { "file": "test/query.t.sol", "test": "testHasQuery1000Keys", "name": "HasQuery with 1000 keys", - "gasUsed": 5764591 + "gasUsed": 5764585 }, { "file": "test/query.t.sol", "test": "testHasQuery100Keys", "name": "HasQuery with 100 keys", - "gasUsed": 537544 + "gasUsed": 537538 }, { "file": "test/query.t.sol", @@ -261,7 +261,7 @@ "file": "test/query.t.sol", "test": "testNotValueQuery", "name": "NotValueQuery", - "gasUsed": 46812 + "gasUsed": 46806 }, { "file": "test/StandardDelegationsModule.t.sol", @@ -309,18 +309,18 @@ "file": "test/UniqueEntityModule.t.sol", "test": "testInstall", "name": "get a unique entity nonce (non-root module)", - "gasUsed": 50371 + "gasUsed": 50365 }, { "file": "test/UniqueEntityModule.t.sol", "test": "testInstallRoot", "name": "installRoot unique entity module", - "gasUsed": 672531 + "gasUsed": 672525 }, { "file": "test/UniqueEntityModule.t.sol", "test": "testInstallRoot", "name": "get a unique entity nonce (root module)", - "gasUsed": 50374 + "gasUsed": 50368 } ] diff --git a/packages/world/gas-report.json b/packages/world/gas-report.json index 22cc4cf7da..54b497614b 100644 --- a/packages/world/gas-report.json +++ b/packages/world/gas-report.json @@ -57,19 +57,19 @@ "file": "test/Factories.t.sol", "test": "testCreate2Factory", "name": "deploy contract via Create2", - "gasUsed": 4738039 + "gasUsed": 4763472 }, { "file": "test/Factories.t.sol", "test": "testWorldFactory", "name": "deploy world via WorldFactory", - "gasUsed": 13016645 + "gasUsed": 13041829 }, { "file": "test/World.t.sol", "test": "testCall", "name": "call a system via the World", - "gasUsed": 12367 + "gasUsed": 12361 }, { "file": "test/World.t.sol", @@ -93,13 +93,13 @@ "file": "test/World.t.sol", "test": "testDeleteRecord", "name": "Delete record", - "gasUsed": 9843 + "gasUsed": 9837 }, { "file": "test/World.t.sol", "test": "testPushToDynamicField", "name": "Push data to the table", - "gasUsed": 85839 + "gasUsed": 85833 }, { "file": "test/World.t.sol", @@ -141,13 +141,13 @@ "file": "test/World.t.sol", "test": "testSetField", "name": "Write data to a table field", - "gasUsed": 36909 + "gasUsed": 36903 }, { "file": "test/World.t.sol", "test": "testSetRecord", "name": "Write data to the table", - "gasUsed": 39053 + "gasUsed": 39047 }, { "file": "test/World.t.sol", @@ -165,25 +165,25 @@ "file": "test/WorldDynamicUpdate.t.sol", "test": "testPopFromDynamicField", "name": "pop 1 address (cold)", - "gasUsed": 25627 + "gasUsed": 25621 }, { "file": "test/WorldDynamicUpdate.t.sol", "test": "testPopFromDynamicField", "name": "pop 1 address (warm)", - "gasUsed": 12773 + "gasUsed": 12767 }, { "file": "test/WorldDynamicUpdate.t.sol", "test": "testSpliceDynamicData", "name": "update in field 1 item (cold)", - "gasUsed": 25987 + "gasUsed": 25981 }, { "file": "test/WorldDynamicUpdate.t.sol", "test": "testSpliceDynamicData", "name": "update in field 1 item (warm)", - "gasUsed": 13188 + "gasUsed": 13182 }, { "file": "test/WorldResourceId.t.sol", diff --git a/packages/world/mud.config.ts b/packages/world/mud.config.ts index 4aa316fc8d..a3f9c8f25f 100644 --- a/packages/world/mud.config.ts +++ b/packages/world/mud.config.ts @@ -103,11 +103,9 @@ export default mudConfig({ }, offchainOnly: true, }, - WorldInitialized: { + CoreModuleAddress: { keySchema: {}, - valueSchema: { - isInitialized: "bool", - }, + valueSchema: "address", }, }, excludeSystems: [ diff --git a/packages/world/src/World.sol b/packages/world/src/World.sol index 324aca5fad..5398ca3f18 100644 --- a/packages/world/src/World.sol +++ b/packages/world/src/World.sol @@ -19,7 +19,7 @@ import { requireInterface } from "./requireInterface.sol"; import { InstalledModules } from "./codegen/tables/InstalledModules.sol"; import { UserDelegationControl } from "./codegen/tables/UserDelegationControl.sol"; import { NamespaceDelegationControl } from "./codegen/tables/NamespaceDelegationControl.sol"; -import { WorldInitialized } from "./codegen/tables/WorldInitialized.sol"; +import { CoreModuleAddress } from "./codegen/tables/CoreModuleAddress.sol"; import { IModule, IModule } from "./IModule.sol"; import { IWorldKernel } from "./IWorldKernel.sol"; @@ -71,11 +71,11 @@ contract World is StoreData, IWorldKernel { } // The World can only be initialized once - if (WorldInitialized.get()) { + if (CoreModuleAddress.get() != address(0)) { revert World_AlreadyInitialized(); } - WorldInitialized.set(true); + CoreModuleAddress.set(address(coreModule)); // Initialize the World by installing the core module _installRootModule(coreModule, new bytes(0)); diff --git a/packages/world/src/codegen/index.sol b/packages/world/src/codegen/index.sol index 4d4774f989..65059df63f 100644 --- a/packages/world/src/codegen/index.sol +++ b/packages/world/src/codegen/index.sol @@ -14,4 +14,4 @@ import { SystemRegistry, SystemRegistryTableId } from "./tables/SystemRegistry.s import { SystemHooks, SystemHooksTableId } from "./tables/SystemHooks.sol"; import { FunctionSelectors, FunctionSelectorsTableId } from "./tables/FunctionSelectors.sol"; import { FunctionSignatures, FunctionSignaturesTableId } from "./tables/FunctionSignatures.sol"; -import { WorldInitialized, WorldInitializedTableId } from "./tables/WorldInitialized.sol"; +import { CoreModuleAddress, CoreModuleAddressTableId } from "./tables/CoreModuleAddress.sol"; diff --git a/packages/world/src/codegen/tables/CoreModuleAddress.sol b/packages/world/src/codegen/tables/CoreModuleAddress.sol new file mode 100644 index 0000000000..0933c2c60c --- /dev/null +++ b/packages/world/src/codegen/tables/CoreModuleAddress.sol @@ -0,0 +1,218 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.21; + +/* Autogenerated file. Do not edit manually. */ + +// Import schema type +import { SchemaType } from "@latticexyz/schema-type/src/solidity/SchemaType.sol"; + +// Import store internals +import { IStore } from "@latticexyz/store/src/IStore.sol"; +import { StoreSwitch } from "@latticexyz/store/src/StoreSwitch.sol"; +import { StoreCore } from "@latticexyz/store/src/StoreCore.sol"; +import { Bytes } from "@latticexyz/store/src/Bytes.sol"; +import { Memory } from "@latticexyz/store/src/Memory.sol"; +import { SliceLib } from "@latticexyz/store/src/Slice.sol"; +import { EncodeArray } from "@latticexyz/store/src/tightcoder/EncodeArray.sol"; +import { FieldLayout, FieldLayoutLib } from "@latticexyz/store/src/FieldLayout.sol"; +import { Schema, SchemaLib } from "@latticexyz/store/src/Schema.sol"; +import { PackedCounter, PackedCounterLib } from "@latticexyz/store/src/PackedCounter.sol"; +import { ResourceId } from "@latticexyz/store/src/ResourceId.sol"; +import { RESOURCE_TABLE, RESOURCE_OFFCHAIN_TABLE } from "@latticexyz/store/src/storeResourceTypes.sol"; + +ResourceId constant _tableId = ResourceId.wrap( + bytes32(abi.encodePacked(RESOURCE_TABLE, bytes14("world"), bytes16("CoreModuleAddres"))) +); +ResourceId constant CoreModuleAddressTableId = _tableId; + +FieldLayout constant _fieldLayout = FieldLayout.wrap( + 0x0014010014000000000000000000000000000000000000000000000000000000 +); + +library CoreModuleAddress { + /** + * @notice Get the table values' field layout. + * @return _fieldLayout The field layout for the table. + */ + function getFieldLayout() internal pure returns (FieldLayout) { + return _fieldLayout; + } + + /** + * @notice Get the table's key schema. + * @return _keySchema The key schema for the table. + */ + function getKeySchema() internal pure returns (Schema) { + SchemaType[] memory _keySchema = new SchemaType[](0); + + return SchemaLib.encode(_keySchema); + } + + /** + * @notice Get the table's value schema. + * @return _valueSchema The value schema for the table. + */ + function getValueSchema() internal pure returns (Schema) { + SchemaType[] memory _valueSchema = new SchemaType[](1); + _valueSchema[0] = SchemaType.ADDRESS; + + return SchemaLib.encode(_valueSchema); + } + + /** + * @notice Get the table's key field names. + * @return keyNames An array of strings with the names of key fields. + */ + function getKeyNames() internal pure returns (string[] memory keyNames) { + keyNames = new string[](0); + } + + /** + * @notice Get the table's value field names. + * @return fieldNames An array of strings with the names of value fields. + */ + function getFieldNames() internal pure returns (string[] memory fieldNames) { + fieldNames = new string[](1); + fieldNames[0] = "value"; + } + + /** + * @notice Register the table with its config. + */ + function register() internal { + StoreSwitch.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + } + + /** + * @notice Register the table with its config. + */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + } + + /** + * @notice Get value. + */ + function getValue() internal view returns (address value) { + bytes32[] memory _keyTuple = new bytes32[](0); + + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (address(bytes20(_blob))); + } + + /** + * @notice Get value. + */ + function _getValue() internal view returns (address value) { + bytes32[] memory _keyTuple = new bytes32[](0); + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (address(bytes20(_blob))); + } + + /** + * @notice Get value. + */ + function get() internal view returns (address value) { + bytes32[] memory _keyTuple = new bytes32[](0); + + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (address(bytes20(_blob))); + } + + /** + * @notice Get value. + */ + function _get() internal view returns (address value) { + bytes32[] memory _keyTuple = new bytes32[](0); + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (address(bytes20(_blob))); + } + + /** + * @notice Set value. + */ + function setValue(address value) internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreSwitch.setStaticField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); + } + + /** + * @notice Set value. + */ + function _setValue(address value) internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreCore.setStaticField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); + } + + /** + * @notice Set value. + */ + function set(address value) internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreSwitch.setStaticField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); + } + + /** + * @notice Set value. + */ + function _set(address value) internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreCore.setStaticField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); + } + + /** + * @notice Delete all data for given keys. + */ + function deleteRecord() internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreSwitch.deleteRecord(_tableId, _keyTuple); + } + + /** + * @notice Delete all data for given keys. + */ + function _deleteRecord() internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /** + * @notice Tightly pack static (fixed length) data using this table's schema. + * @return The static data, encoded into a sequence of bytes. + */ + function encodeStatic(address value) internal pure returns (bytes memory) { + return abi.encodePacked(value); + } + + /** + * @notice Encode all of a record's fields. + * @return The static (fixed length) data, encoded into a sequence of bytes. + * @return The lengths of the dynamic fields (packed into a single bytes32 value). + * @return The dynamic (variable length) data, encoded into a sequence of bytes. + */ + function encode(address value) internal pure returns (bytes memory, PackedCounter, bytes memory) { + bytes memory _staticData = encodeStatic(value); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; + + return (_staticData, _encodedLengths, _dynamicData); + } + + /** + * @notice Encode keys as a bytes32 array using this table's field layout. + */ + function encodeKeyTuple() internal pure returns (bytes32[] memory) { + bytes32[] memory _keyTuple = new bytes32[](0); + + return _keyTuple; + } +} diff --git a/packages/world/src/modules/core/CoreModule.sol b/packages/world/src/modules/core/CoreModule.sol index 12c8e1f550..aaeb5b283d 100644 --- a/packages/world/src/modules/core/CoreModule.sol +++ b/packages/world/src/modules/core/CoreModule.sol @@ -28,7 +28,7 @@ import { FunctionSelectors } from "../../codegen/tables/FunctionSelectors.sol"; import { FunctionSignatures } from "../../codegen/tables/FunctionSignatures.sol"; import { SystemHooks } from "../../codegen/tables/SystemHooks.sol"; import { SystemRegistry } from "../../codegen/tables/SystemRegistry.sol"; -import { WorldInitialized } from "../../codegen/tables/WorldInitialized.sol"; +import { CoreModuleAddress } from "../../codegen/tables/CoreModuleAddress.sol"; import { Balances } from "../../codegen/tables/Balances.sol"; import { WorldRegistrationSystem } from "./implementations/WorldRegistrationSystem.sol"; @@ -92,7 +92,7 @@ contract CoreModule is Module { FunctionSignatures.register(); SystemHooks.register(); SystemRegistry.register(); - WorldInitialized.register(); + CoreModuleAddress.register(); ResourceIds._setExists(ROOT_NAMESPACE_ID, true); NamespaceOwner._set(ROOT_NAMESPACE_ID, _msgSender());