From 37c228c63235e184a40623d9bb1f6494abdf25e4 Mon Sep 17 00:00:00 2001 From: yonada Date: Thu, 18 Jan 2024 17:16:01 +0000 Subject: [PATCH] refactor(store,world,world-modules): code suggestions [N-08] (#2140) --- .changeset/happy-ants-lay.md | 6 + .changeset/little-cherries-rule.md | 5 + .changeset/little-cobras-yell.md | 5 + .changeset/modern-impalas-stare.md | 7 + .changeset/nasty-owls-sneeze.md | 7 + .changeset/soft-panthers-develop.md | 7 + .changeset/spotty-balloons-itch.md | 5 + .changeset/thin-days-sparkle.md | 5 + .changeset/three-llamas-sin.md | 5 + .changeset/weak-otters-turn.md | 5 + docs/pages/store/reference/store-hook.mdx | 17 -- packages/store/gas-report.json | 160 +++++++++--------- packages/store/src/Bytes.sol | 60 +++---- packages/store/src/FieldLayout.sol | 2 +- packages/store/src/IERC165.sol | 2 - packages/store/src/IStoreHook.sol | 10 -- packages/store/src/Memory.sol | 8 +- packages/store/src/ResourceId.sol | 2 + packages/store/src/Schema.sol | 52 +++--- packages/store/src/Slice.sol | 20 +-- packages/store/src/Storage.sol | 26 +-- packages/store/src/StoreCore.sol | 5 +- packages/store/src/StoreHook.sol | 6 +- packages/store/src/leftMask.sol | 31 ---- packages/store/src/rightMask.sol | 23 +++ packages/store/src/tightcoder/TightCoder.sol | 4 +- packages/store/test/ResourceId.t.sol | 4 +- packages/store/test/Schema.t.sol | 93 +++++++++- packages/store/test/StoreCore.t.sol | 4 +- packages/store/test/leftMask.t.sol | 35 ---- packages/store/test/rightMask.t.sol | 35 ++++ packages/world-modules/gas-report.json | 96 +++++------ .../src/modules/utils/ArrayLib.sol | 2 +- .../test/KeysWithValueModule.t.sol | 3 +- .../world-modules/test/PuppetModule.t.sol | 2 +- .../test/StandardDelegationsModule.t.sol | 4 +- packages/world/gas-report.json | 44 ++--- packages/world/src/Create2.sol | 3 +- packages/world/src/DelegationControl.sol | 14 +- packages/world/src/IDelegationControl.sol | 9 +- packages/world/src/IERC165.sol | 5 - packages/world/src/IModule.sol | 8 - packages/world/src/ISystemHook.sol | 7 - packages/world/src/IWorldContextConsumer.sol | 4 - packages/world/src/Module.sol | 12 +- packages/world/src/SystemHook.sol | 6 +- packages/world/src/World.sol | 4 +- packages/world/src/WorldContext.sol | 12 +- packages/world/src/WorldFactory.sol | 2 +- .../interfaces/IWorldRegistrationSystem.sol | 4 +- .../world/src/modules/core/CoreModule.sol | 5 +- .../implementations/BalanceTransferSystem.sol | 4 +- .../ModuleInstallationSystem.sol | 4 +- .../StoreRegistrationSystem.sol | 4 +- .../WorldRegistrationSystem.sol | 19 +-- packages/world/test/SystemHook.t.sol | 6 +- packages/world/test/World.t.sol | 22 ++- packages/world/test/WorldResourceId.t.sol | 3 +- 58 files changed, 506 insertions(+), 458 deletions(-) create mode 100644 .changeset/happy-ants-lay.md create mode 100644 .changeset/little-cherries-rule.md create mode 100644 .changeset/little-cobras-yell.md create mode 100644 .changeset/modern-impalas-stare.md create mode 100644 .changeset/nasty-owls-sneeze.md create mode 100644 .changeset/soft-panthers-develop.md create mode 100644 .changeset/spotty-balloons-itch.md create mode 100644 .changeset/thin-days-sparkle.md create mode 100644 .changeset/three-llamas-sin.md create mode 100644 .changeset/weak-otters-turn.md delete mode 100644 packages/store/src/leftMask.sol create mode 100644 packages/store/src/rightMask.sol delete mode 100644 packages/store/test/leftMask.t.sol create mode 100644 packages/store/test/rightMask.t.sol diff --git a/.changeset/happy-ants-lay.md b/.changeset/happy-ants-lay.md new file mode 100644 index 0000000000..1f1ecb5a8d --- /dev/null +++ b/.changeset/happy-ants-lay.md @@ -0,0 +1,6 @@ +--- +"@latticexyz/store": patch +"@latticexyz/world": patch +--- + +Refactored various files to specify integers in a hex base instead of decimals. diff --git a/.changeset/little-cherries-rule.md b/.changeset/little-cherries-rule.md new file mode 100644 index 0000000000..58bc920e71 --- /dev/null +++ b/.changeset/little-cherries-rule.md @@ -0,0 +1,5 @@ +--- +"@latticexyz/world": patch +--- + +Made the `coreModule` variable in `WorldFactory` immutable. diff --git a/.changeset/little-cobras-yell.md b/.changeset/little-cobras-yell.md new file mode 100644 index 0000000000..0f2a769f5e --- /dev/null +++ b/.changeset/little-cobras-yell.md @@ -0,0 +1,5 @@ +--- +"@latticexyz/world": patch +--- + +Removed the unnecessary `extcodesize` check from the `Create2` library. diff --git a/.changeset/modern-impalas-stare.md b/.changeset/modern-impalas-stare.md new file mode 100644 index 0000000000..25eaa7c27f --- /dev/null +++ b/.changeset/modern-impalas-stare.md @@ -0,0 +1,7 @@ +--- +"@latticexyz/world-modules": patch +"@latticexyz/store": patch +"@latticexyz/world": patch +--- + +Refactored `ResourceId` to use a global Solidity `using` statement. diff --git a/.changeset/nasty-owls-sneeze.md b/.changeset/nasty-owls-sneeze.md new file mode 100644 index 0000000000..1903b2aa4c --- /dev/null +++ b/.changeset/nasty-owls-sneeze.md @@ -0,0 +1,7 @@ +--- +"@latticexyz/world-modules": patch +"@latticexyz/store": patch +"@latticexyz/world": patch +--- + +Refactored EIP165 usages to use the built-in interfaceId property instead of pre-defined constants. diff --git a/.changeset/soft-panthers-develop.md b/.changeset/soft-panthers-develop.md new file mode 100644 index 0000000000..c4b0f65d32 --- /dev/null +++ b/.changeset/soft-panthers-develop.md @@ -0,0 +1,7 @@ +--- +"@latticexyz/world-modules": patch +"@latticexyz/store": patch +"@latticexyz/world": patch +--- + +Refactored various Solidity files to not explicitly initialise variables to zero. diff --git a/.changeset/spotty-balloons-itch.md b/.changeset/spotty-balloons-itch.md new file mode 100644 index 0000000000..bc704d667c --- /dev/null +++ b/.changeset/spotty-balloons-itch.md @@ -0,0 +1,5 @@ +--- +"@latticexyz/store": patch +--- + +Refactored some Store functions to use a right bit mask instead of left. diff --git a/.changeset/thin-days-sparkle.md b/.changeset/thin-days-sparkle.md new file mode 100644 index 0000000000..c174f8de3b --- /dev/null +++ b/.changeset/thin-days-sparkle.md @@ -0,0 +1,5 @@ +--- +"@latticexyz/store": patch +--- + +Simplified a check in `Slice.getSubslice`. diff --git a/.changeset/three-llamas-sin.md b/.changeset/three-llamas-sin.md new file mode 100644 index 0000000000..75d44607b3 --- /dev/null +++ b/.changeset/three-llamas-sin.md @@ -0,0 +1,5 @@ +--- +"@latticexyz/world": patch +--- + +Refactored `WorldContext` to get the world address from `WorldContextConsumerLib` instead of `StoreSwitch`. diff --git a/.changeset/weak-otters-turn.md b/.changeset/weak-otters-turn.md new file mode 100644 index 0000000000..8adae50a53 --- /dev/null +++ b/.changeset/weak-otters-turn.md @@ -0,0 +1,5 @@ +--- +"@latticexyz/store": patch +--- + +Optimised the `Schema.validate` function to decrease gas use. diff --git a/docs/pages/store/reference/store-hook.mdx b/docs/pages/store/reference/store-hook.mdx index e39061c978..62c04a9c15 100644 --- a/docs/pages/store/reference/store-hook.mdx +++ b/docs/pages/store/reference/store-hook.mdx @@ -1,22 +1,5 @@ [//]: # "This file is autogenerated, do not change manually" -## Constants - -[Git Source](https://github.com/latticexyz/mud/blob/main/packages/store/src/IStoreHook.sol) - -### STORE_HOOK_INTERFACE_ID - -```solidity -bytes4 constant STORE_HOOK_INTERFACE_ID = IStoreHook.onBeforeSetRecord.selector ^ - IStoreHook.onAfterSetRecord.selector ^ - IStoreHook.onBeforeSpliceStaticData.selector ^ - IStoreHook.onAfterSpliceStaticData.selector ^ - IStoreHook.onBeforeSpliceDynamicData.selector ^ - IStoreHook.onAfterSpliceDynamicData.selector ^ - IStoreHook.onBeforeDeleteRecord.selector ^ - IStoreHook.onAfterDeleteRecord.selector; -``` - ## IStoreHook [Git Source](https://github.com/latticexyz/mud/blob/main/packages/store/src/IStoreHook.sol) diff --git a/packages/store/gas-report.json b/packages/store/gas-report.json index 8180a7db2b..1c7df82c5a 100644 --- a/packages/store/gas-report.json +++ b/packages/store/gas-report.json @@ -9,25 +9,25 @@ "file": "test/Bytes.t.sol", "test": "testSlice32", "name": "slice bytes32 with offset 10", - "gasUsed": 68 + "gasUsed": 13 }, { "file": "test/Callbacks.t.sol", "test": "testSetAndGet", "name": "Callbacks: set field", - "gasUsed": 56271 + "gasUsed": 56262 }, { "file": "test/Callbacks.t.sol", "test": "testSetAndGet", "name": "Callbacks: get field (warm)", - "gasUsed": 2842 + "gasUsed": 2833 }, { "file": "test/Callbacks.t.sol", "test": "testSetAndGet", "name": "Callbacks: push 1 element", - "gasUsed": 32530 + "gasUsed": 32521 }, { "file": "test/FieldLayout.t.sol", @@ -141,7 +141,7 @@ "file": "test/Gas.t.sol", "test": "testCompareAbiEncodeVsCustom", "name": "custom decode", - "gasUsed": 1969 + "gasUsed": 1954 }, { "file": "test/Gas.t.sol", @@ -237,7 +237,7 @@ "file": "test/GasStorageLoad.t.sol", "test": "testCompareStorageLoadMUD", "name": "MUD storage load (cold, 1 word, partial)", - "gasUsed": 2484 + "gasUsed": 2481 }, { "file": "test/GasStorageLoad.t.sol", @@ -261,7 +261,7 @@ "file": "test/GasStorageLoad.t.sol", "test": "testCompareStorageLoadMUD", "name": "MUD storage load (warm, 1 word, partial)", - "gasUsed": 484 + "gasUsed": 481 }, { "file": "test/GasStorageLoad.t.sol", @@ -321,7 +321,7 @@ "file": "test/KeyEncoding.t.sol", "test": "testRegisterAndGetFieldLayout", "name": "register KeyEncoding table", - "gasUsed": 727536 + "gasUsed": 727151 }, { "file": "test/Mixed.t.sol", @@ -333,49 +333,49 @@ "file": "test/Mixed.t.sol", "test": "testDeleteExternalCold", "name": "delete record from Mixed (external, cold)", - "gasUsed": 24372 + "gasUsed": 24363 }, { "file": "test/Mixed.t.sol", "test": "testDeleteInternalCold", "name": "delete record from Mixed (internal, cold)", - "gasUsed": 19168 + "gasUsed": 19159 }, { "file": "test/Mixed.t.sol", "test": "testSetGetDeleteExternal", "name": "set record in Mixed (external, cold)", - "gasUsed": 108528 + "gasUsed": 108513 }, { "file": "test/Mixed.t.sol", "test": "testSetGetDeleteExternal", "name": "get record from Mixed (external, warm)", - "gasUsed": 7001 + "gasUsed": 6977 }, { "file": "test/Mixed.t.sol", "test": "testSetGetDeleteExternal", "name": "delete record from Mixed (external, warm)", - "gasUsed": 8688 + "gasUsed": 8679 }, { "file": "test/Mixed.t.sol", "test": "testSetGetDeleteInternal", "name": "set record in Mixed (internal, cold)", - "gasUsed": 103282 + "gasUsed": 103267 }, { "file": "test/Mixed.t.sol", "test": "testSetGetDeleteInternal", "name": "get record from Mixed (internal, warm)", - "gasUsed": 6687 + "gasUsed": 6663 }, { "file": "test/Mixed.t.sol", "test": "testSetGetDeleteInternal", "name": "delete record from Mixed (internal, warm)", - "gasUsed": 7481 + "gasUsed": 7472 }, { "file": "test/PackedCounter.t.sol", @@ -477,7 +477,7 @@ "file": "test/Schema.t.sol", "test": "testValidate", "name": "validate schema", - "gasUsed": 13777 + "gasUsed": 12485 }, { "file": "test/Slice.t.sol", @@ -501,13 +501,13 @@ "file": "test/Slice.t.sol", "test": "testSubslice", "name": "subslice bytes (no copy) [1:4]", - "gasUsed": 324 + "gasUsed": 318 }, { "file": "test/Slice.t.sol", "test": "testSubslice", "name": "subslice bytes (no copy) [4:37]", - "gasUsed": 324 + "gasUsed": 318 }, { "file": "test/Slice.t.sol", @@ -519,7 +519,7 @@ "file": "test/Slice.t.sol", "test": "testToBytes", "name": "Slice (2 bytes) to bytes memory", - "gasUsed": 534 + "gasUsed": 531 }, { "file": "test/Slice.t.sol", @@ -531,7 +531,7 @@ "file": "test/Slice.t.sol", "test": "testToBytes", "name": "Slice (34 bytes) to bytes memory", - "gasUsed": 750 + "gasUsed": 747 }, { "file": "test/Slice.t.sol", @@ -561,37 +561,37 @@ "file": "test/Storage.t.sol", "test": "testStoreLoad", "name": "store 34 bytes over 3 storage slots (with offset and safeTrail))", - "gasUsed": 23028 + "gasUsed": 23025 }, { "file": "test/Storage.t.sol", "test": "testStoreLoad", "name": "load 34 bytes over 3 storage slots (with offset and safeTrail))", - "gasUsed": 885 + "gasUsed": 871 }, { "file": "test/StoreCoreDynamic.t.sol", "test": "testGetDynamicFieldSlice", "name": "get field slice (cold, 1 slot)", - "gasUsed": 5569 + "gasUsed": 5566 }, { "file": "test/StoreCoreDynamic.t.sol", "test": "testGetDynamicFieldSlice", "name": "get field slice (warm, 1 slot)", - "gasUsed": 1671 + "gasUsed": 1668 }, { "file": "test/StoreCoreDynamic.t.sol", "test": "testGetDynamicFieldSlice", "name": "get field slice (semi-cold, 1 slot)", - "gasUsed": 3664 + "gasUsed": 3661 }, { "file": "test/StoreCoreDynamic.t.sol", "test": "testGetDynamicFieldSlice", "name": "get field slice (warm, 2 slots)", - "gasUsed": 3891 + "gasUsed": 3885 }, { "file": "test/StoreCoreDynamic.t.sol", @@ -621,37 +621,37 @@ "file": "test/StoreCoreDynamic.t.sol", "test": "testPopFromSecondField", "name": "pop from field (cold, 1 slot, 1 uint32 item)", - "gasUsed": 18041 + "gasUsed": 18035 }, { "file": "test/StoreCoreDynamic.t.sol", "test": "testPopFromSecondField", "name": "pop from field (warm, 1 slot, 1 uint32 item)", - "gasUsed": 12049 + "gasUsed": 12043 }, { "file": "test/StoreCoreDynamic.t.sol", "test": "testPopFromThirdField", "name": "pop from field (cold, 2 slots, 10 uint32 items)", - "gasUsed": 15810 + "gasUsed": 15804 }, { "file": "test/StoreCoreDynamic.t.sol", "test": "testPopFromThirdField", "name": "pop from field (warm, 2 slots, 10 uint32 items)", - "gasUsed": 11818 + "gasUsed": 11812 }, { "file": "test/StoreCoreGas.t.sol", "test": "testAccessEmptyData", "name": "access non-existing record", - "gasUsed": 7077 + "gasUsed": 7074 }, { "file": "test/StoreCoreGas.t.sol", "test": "testAccessEmptyData", "name": "access static field of non-existing record", - "gasUsed": 1333 + "gasUsed": 1330 }, { "file": "test/StoreCoreGas.t.sol", @@ -669,7 +669,7 @@ "file": "test/StoreCoreGas.t.sol", "test": "testDeleteData", "name": "delete record (complex data, 3 slots)", - "gasUsed": 8015 + "gasUsed": 8006 }, { "file": "test/StoreCoreGas.t.sol", @@ -705,67 +705,67 @@ "file": "test/StoreCoreGas.t.sol", "test": "testHooks", "name": "register subscriber", - "gasUsed": 57954 + "gasUsed": 57945 }, { "file": "test/StoreCoreGas.t.sol", "test": "testHooks", "name": "set record on table with subscriber", - "gasUsed": 72385 + "gasUsed": 72364 }, { "file": "test/StoreCoreGas.t.sol", "test": "testHooks", "name": "set static field on table with subscriber", - "gasUsed": 19837 + "gasUsed": 19816 }, { "file": "test/StoreCoreGas.t.sol", "test": "testHooks", "name": "delete record on table with subscriber", - "gasUsed": 18595 + "gasUsed": 18574 }, { "file": "test/StoreCoreGas.t.sol", "test": "testHooksDynamicData", "name": "register subscriber", - "gasUsed": 57954 + "gasUsed": 57945 }, { "file": "test/StoreCoreGas.t.sol", "test": "testHooksDynamicData", "name": "set (dynamic) record on table with subscriber", - "gasUsed": 165525 + "gasUsed": 165498 }, { "file": "test/StoreCoreGas.t.sol", "test": "testHooksDynamicData", "name": "set (dynamic) field on table with subscriber", - "gasUsed": 24446 + "gasUsed": 24425 }, { "file": "test/StoreCoreGas.t.sol", "test": "testHooksDynamicData", "name": "delete (dynamic) record on table with subscriber", - "gasUsed": 20261 + "gasUsed": 20240 }, { "file": "test/StoreCoreGas.t.sol", "test": "testPushToDynamicField", "name": "push to field (1 slot, 1 uint32 item)", - "gasUsed": 9475 + "gasUsed": 9469 }, { "file": "test/StoreCoreGas.t.sol", "test": "testPushToDynamicField", "name": "push to field (2 slots, 10 uint32 items)", - "gasUsed": 32150 + "gasUsed": 32141 }, { "file": "test/StoreCoreGas.t.sol", "test": "testRegisterAndGetFieldLayout", "name": "StoreCore: register table", - "gasUsed": 651200 + "gasUsed": 650861 }, { "file": "test/StoreCoreGas.t.sol", @@ -789,13 +789,13 @@ "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetDynamicData", "name": "set complex record with dynamic data (4 slots)", - "gasUsed": 102513 + "gasUsed": 102498 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetDynamicData", "name": "get complex record with dynamic data (4 slots)", - "gasUsed": 4244 + "gasUsed": 4235 }, { "file": "test/StoreCoreGas.t.sol", @@ -831,73 +831,73 @@ "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetField", "name": "set static field (1 slot)", - "gasUsed": 31252 + "gasUsed": 31243 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetField", "name": "get static field (1 slot)", - "gasUsed": 1333 + "gasUsed": 1330 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetField", "name": "set static field (overlap 2 slot)", - "gasUsed": 29908 + "gasUsed": 29899 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetField", "name": "get static field (overlap 2 slot)", - "gasUsed": 1889 + "gasUsed": 1883 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetField", "name": "set dynamic field (1 slot, first dynamic field)", - "gasUsed": 53961 + "gasUsed": 53952 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetField", "name": "get dynamic field (1 slot, first dynamic field)", - "gasUsed": 2230 + "gasUsed": 2227 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetField", "name": "set dynamic field (1 slot, second dynamic field)", - "gasUsed": 32186 + "gasUsed": 32177 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetField", "name": "get dynamic field (1 slot, second dynamic field)", - "gasUsed": 2233 + "gasUsed": 2230 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetStaticData", "name": "set static record (1 slot)", - "gasUsed": 32782 + "gasUsed": 32773 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetStaticData", "name": "get static record (1 slot)", - "gasUsed": 1555 + "gasUsed": 1552 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetStaticDataSpanningWords", "name": "set static record (2 slots)", - "gasUsed": 55289 + "gasUsed": 55280 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetStaticDataSpanningWords", "name": "get static record (2 slots)", - "gasUsed": 1740 + "gasUsed": 1737 }, { "file": "test/StoreCoreGas.t.sol", @@ -909,13 +909,13 @@ "file": "test/StoreCoreGas.t.sol", "test": "testUpdateInDynamicField", "name": "update in field (1 slot, 1 uint32 item)", - "gasUsed": 8835 + "gasUsed": 8829 }, { "file": "test/StoreCoreGas.t.sol", "test": "testUpdateInDynamicField", "name": "push to field (2 slots, 6 uint64 items)", - "gasUsed": 9282 + "gasUsed": 9273 }, { "file": "test/StoreHook.t.sol", @@ -945,85 +945,85 @@ "file": "test/StoreHooks.t.sol", "test": "testOneSlot", "name": "StoreHooks: set field with one elements (cold)", - "gasUsed": 58275 + "gasUsed": 58266 }, { "file": "test/StoreHooks.t.sol", "test": "testTable", "name": "StoreHooks: set field (cold)", - "gasUsed": 58275 + "gasUsed": 58266 }, { "file": "test/StoreHooks.t.sol", "test": "testTable", "name": "StoreHooks: get field (warm)", - "gasUsed": 2844 + "gasUsed": 2835 }, { "file": "test/StoreHooks.t.sol", "test": "testTable", "name": "StoreHooks: push 1 element (cold)", - "gasUsed": 12626 + "gasUsed": 12617 }, { "file": "test/StoreHooks.t.sol", "test": "testTable", "name": "StoreHooks: pop 1 element (warm)", - "gasUsed": 9958 + "gasUsed": 9952 }, { "file": "test/StoreHooks.t.sol", "test": "testTable", "name": "StoreHooks: push 1 element (warm)", - "gasUsed": 10646 + "gasUsed": 10637 }, { "file": "test/StoreHooks.t.sol", "test": "testTable", "name": "StoreHooks: update 1 element (warm)", - "gasUsed": 29886 + "gasUsed": 29877 }, { "file": "test/StoreHooks.t.sol", "test": "testTable", "name": "StoreHooks: delete record (warm)", - "gasUsed": 10425 + "gasUsed": 10419 }, { "file": "test/StoreHooks.t.sol", "test": "testTable", "name": "StoreHooks: set field (warm)", - "gasUsed": 30427 + "gasUsed": 30418 }, { "file": "test/StoreHooks.t.sol", "test": "testThreeSlots", "name": "StoreHooks: set field with three elements (cold)", - "gasUsed": 80966 + "gasUsed": 80957 }, { "file": "test/StoreHooks.t.sol", "test": "testTwoSlots", "name": "StoreHooks: set field with two elements (cold)", - "gasUsed": 80878 + "gasUsed": 80869 }, { "file": "test/StoreHooksColdLoad.t.sol", "test": "testDelete", "name": "StoreHooks: delete record (cold)", - "gasUsed": 19283 + "gasUsed": 19277 }, { "file": "test/StoreHooksColdLoad.t.sol", "test": "testGet", "name": "StoreHooks: get field (cold)", - "gasUsed": 8841 + "gasUsed": 8832 }, { "file": "test/StoreHooksColdLoad.t.sol", "test": "testGetItem", "name": "StoreHooks: get 1 element (cold)", - "gasUsed": 8446 + "gasUsed": 8443 }, { "file": "test/StoreHooksColdLoad.t.sol", @@ -1035,13 +1035,13 @@ "file": "test/StoreHooksColdLoad.t.sol", "test": "testPop", "name": "StoreHooks: pop 1 element (cold)", - "gasUsed": 18390 + "gasUsed": 18384 }, { "file": "test/StoreHooksColdLoad.t.sol", "test": "testUpdate", "name": "StoreHooks: update 1 element (cold)", - "gasUsed": 20333 + "gasUsed": 20324 }, { "file": "test/StoreSwitch.t.sol", @@ -1107,18 +1107,18 @@ "file": "test/Vector2.t.sol", "test": "testRegisterAndGetFieldLayout", "name": "register Vector2 field layout", - "gasUsed": 451196 + "gasUsed": 450995 }, { "file": "test/Vector2.t.sol", "test": "testSetAndGet", "name": "set Vector2 record", - "gasUsed": 33684 + "gasUsed": 33675 }, { "file": "test/Vector2.t.sol", "test": "testSetAndGet", "name": "get Vector2 record", - "gasUsed": 2539 + "gasUsed": 2536 } ] diff --git a/packages/store/src/Bytes.sol b/packages/store/src/Bytes.sol index 4da11f7ff3..df13cc1e92 100644 --- a/packages/store/src/Bytes.sol +++ b/packages/store/src/Bytes.sol @@ -61,139 +61,119 @@ library Bytes { * @dev Extracts a single byte from a bytes blob starting at a specific position. * @param data The bytes blob from which a byte is to be extracted. * @param start The starting position within the bytes blob for extraction. - * @return The extracted bytes1 value from the specified position in the bytes blob. + * @return output The extracted bytes1 value from the specified position in the bytes blob. */ - function slice1(bytes memory data, uint256 start) internal pure returns (bytes1) { - bytes1 output; + function slice1(bytes memory data, uint256 start) internal pure returns (bytes1 output) { assembly { output := mload(add(add(data, 0x20), start)) } - return output; } /** * @dev Extracts a single byte from a bytes32 value starting at a specific position. * @param data The bytes32 value from which a byte is to be extracted. * @param start The starting position within the bytes32 value for extraction. - * @return The extracted bytes1 value from the specified position in the bytes32 value. + * @return output The extracted bytes1 value from the specified position in the bytes32 value. */ - function slice1(bytes32 data, uint256 start) internal pure returns (bytes1) { - bytes1 output; + function slice1(bytes32 data, uint256 start) internal pure returns (bytes1 output) { assembly { output := shl(mul(8, start), data) } - return output; } /** * @dev Extracts a 2-byte sequence from a bytes blob starting at a specific position. * @param data The bytes blob from which a 2-byte sequence is to be extracted. * @param start The starting position within the bytes blob for extraction. - * @return The extracted bytes2 value from the specified position in the bytes blob. + * @return output The extracted bytes2 value from the specified position in the bytes blob. */ - function slice2(bytes memory data, uint256 start) internal pure returns (bytes2) { - bytes2 output; + function slice2(bytes memory data, uint256 start) internal pure returns (bytes2 output) { assembly { output := mload(add(add(data, 0x20), start)) } - return output; } /** * @dev Extracts a 2-byte sequence from a bytes32 value starting at a specific position. * @param data The bytes32 value from which a 2-byte sequence is to be extracted. * @param start The starting position within the bytes32 value for extraction. - * @return The extracted bytes2 value from the specified position in the bytes32 value. + * @return output The extracted bytes2 value from the specified position in the bytes32 value. */ - function slice2(bytes32 data, uint256 start) internal pure returns (bytes2) { - bytes2 output; + function slice2(bytes32 data, uint256 start) internal pure returns (bytes2 output) { assembly { output := shl(mul(8, start), data) } - return output; } /** * @dev Extracts a 4-byte sequence from a bytes blob starting at a specific position. * @param data The bytes blob from which a 4-byte sequence is to be extracted. * @param start The starting position within the bytes blob for extraction. - * @return The extracted bytes4 value from the specified position in the bytes blob. + * @return output The extracted bytes4 value from the specified position in the bytes blob. */ - function slice4(bytes memory data, uint256 start) internal pure returns (bytes4) { - bytes4 output; + function slice4(bytes memory data, uint256 start) internal pure returns (bytes4 output) { assembly { output := mload(add(add(data, 0x20), start)) } - return output; } /** * @dev Extracts a 5-byte sequence from a bytes blob starting at a specific position. * @param data The bytes blob from which a 5-byte sequence is to be extracted. * @param start The starting position within the bytes blob for extraction. - * @return The extracted bytes5 value from the specified position in the bytes blob. + * @return output The extracted bytes5 value from the specified position in the bytes blob. */ - function slice5(bytes memory data, uint256 start) internal pure returns (bytes5) { - bytes5 output; + function slice5(bytes memory data, uint256 start) internal pure returns (bytes5 output) { assembly { output := mload(add(add(data, 0x20), start)) } - return output; } /** * @dev Extracts a 8-byte sequence from a bytes blob starting at a specific position. * @param data The bytes blob from which a 8-byte sequence is to be extracted. * @param start The starting position within the bytes blob for extraction. - * @return The extracted bytes8 value from the specified position in the bytes blob. + * @return output The extracted bytes8 value from the specified position in the bytes blob. */ - function slice8(bytes memory data, uint256 start) internal pure returns (bytes8) { - bytes8 output; + function slice8(bytes memory data, uint256 start) internal pure returns (bytes8 output) { assembly { output := mload(add(add(data, 0x20), start)) } - return output; } /** * @dev Extracts a 16-byte sequence from a bytes blob starting at a specific position. * @param data The bytes blob from which a 16-byte sequence is to be extracted. * @param start The starting position within the bytes blob for extraction. - * @return The extracted bytes16 value from the specified position in the bytes blob. + * @return output The extracted bytes16 value from the specified position in the bytes blob. */ - function slice16(bytes memory data, uint256 start) internal pure returns (bytes16) { - bytes16 output; + function slice16(bytes memory data, uint256 start) internal pure returns (bytes16 output) { assembly { output := mload(add(add(data, 0x20), start)) } - return output; } /** * @dev Extracts a 20-byte sequence from a bytes blob starting at a specific position. * @param data The bytes blob from which a 20-byte sequence is to be extracted. * @param start The starting position within the bytes blob for extraction. - * @return The extracted bytes20 value from the specified position in the bytes blob. + * @return output The extracted bytes20 value from the specified position in the bytes blob. */ - function slice20(bytes memory data, uint256 start) internal pure returns (bytes20) { - bytes20 output; + function slice20(bytes memory data, uint256 start) internal pure returns (bytes20 output) { assembly { output := mload(add(add(data, 0x20), start)) } - return output; } /** * @dev Extracts a 32-byte sequence from a bytes blob starting at a specific position. * @param data The bytes blob from which a 32-byte sequence is to be extracted. * @param start The starting position within the bytes blob for extraction. - * @return The extracted bytes32 value from the specified position in the bytes blob. + * @return output The extracted bytes32 value from the specified position in the bytes blob. */ - function slice32(bytes memory data, uint256 start) internal pure returns (bytes32) { - bytes32 output; + function slice32(bytes memory data, uint256 start) internal pure returns (bytes32 output) { assembly { output := mload(add(add(data, 0x20), start)) } - return output; } } diff --git a/packages/store/src/FieldLayout.sol b/packages/store/src/FieldLayout.sol index ae5d66ed26..1a5fae43cc 100644 --- a/packages/store/src/FieldLayout.sol +++ b/packages/store/src/FieldLayout.sol @@ -50,7 +50,7 @@ library FieldLayoutLib { revert FieldLayoutLib_TooManyDynamicFields(numDynamicFields, MAX_DYNAMIC_FIELDS); // Compute the total static length and store the field lengths in the encoded fieldLayout - for (uint256 i = 0; i < _staticFieldLengths.length; ) { + for (uint256 i; i < _staticFieldLengths.length; ) { uint256 staticByteLength = _staticFieldLengths[i]; if (staticByteLength == 0) { revert FieldLayoutLib_StaticLengthIsZero(i); diff --git a/packages/store/src/IERC165.sol b/packages/store/src/IERC165.sol index 5a14a51e2a..d0aee5997a 100644 --- a/packages/store/src/IERC165.sol +++ b/packages/store/src/IERC165.sol @@ -1,8 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.21; -bytes4 constant ERC165_INTERFACE_ID = IERC165.supportsInterface.selector; - // See https://eips.ethereum.org/EIPS/eip-165 interface IERC165 { /** diff --git a/packages/store/src/IStoreHook.sol b/packages/store/src/IStoreHook.sol index 8974c80b11..cd3847fee4 100644 --- a/packages/store/src/IStoreHook.sol +++ b/packages/store/src/IStoreHook.sol @@ -6,16 +6,6 @@ import { IERC165 } from "./IERC165.sol"; import { PackedCounter } from "./PackedCounter.sol"; import { ResourceId } from "./ResourceId.sol"; -// ERC-165 Interface ID (see https://eips.ethereum.org/EIPS/eip-165) -bytes4 constant STORE_HOOK_INTERFACE_ID = IStoreHook.onBeforeSetRecord.selector ^ - IStoreHook.onAfterSetRecord.selector ^ - IStoreHook.onBeforeSpliceStaticData.selector ^ - IStoreHook.onAfterSpliceStaticData.selector ^ - IStoreHook.onBeforeSpliceDynamicData.selector ^ - IStoreHook.onAfterSpliceDynamicData.selector ^ - IStoreHook.onBeforeDeleteRecord.selector ^ - IStoreHook.onAfterDeleteRecord.selector; - interface IStoreHook is IERC165 { /// @notice Error emitted when a function is not implemented. error StoreHook_NotImplemented(); diff --git a/packages/store/src/Memory.sol b/packages/store/src/Memory.sol index 4789e9cfbf..9dda2a562a 100644 --- a/packages/store/src/Memory.sol +++ b/packages/store/src/Memory.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.21; -import { leftMask } from "./leftMask.sol"; +import { rightMask } from "./rightMask.sol"; /** * @title Memory Operations @@ -47,16 +47,16 @@ library Memory { if (length == 0) return; // Copy the 0-31 length tail - uint256 mask = leftMask(length); + uint256 mask = rightMask(length); /// @solidity memory-safe-assembly assembly { mstore( toPointer, or( // store the left part - and(mload(fromPointer), mask), + and(mload(fromPointer), not(mask)), // preserve the right part - and(mload(toPointer), not(mask)) + and(mload(toPointer), mask) ) ) } diff --git a/packages/store/src/ResourceId.sol b/packages/store/src/ResourceId.sol index 6f337dec07..fced287336 100644 --- a/packages/store/src/ResourceId.sol +++ b/packages/store/src/ResourceId.sol @@ -8,6 +8,8 @@ pragma solidity >=0.8.21; */ type ResourceId is bytes32; +using ResourceIdInstance for ResourceId global; + /// @dev Number of bits reserved for the type in the ResourceId. uint256 constant TYPE_BITS = 2 * 8; // 2 bytes * 8 bits per byte diff --git a/packages/store/src/Schema.sol b/packages/store/src/Schema.sol index 8b06f0858c..211bf5304c 100644 --- a/packages/store/src/Schema.sol +++ b/packages/store/src/Schema.sol @@ -29,19 +29,19 @@ library SchemaLib { /** * @notice Encodes a given schema into a single bytes32. - * @param _schema The list of SchemaTypes that constitute the schema. + * @param schemas The list of SchemaTypes that constitute the schema. * @return The encoded Schema. */ - function encode(SchemaType[] memory _schema) internal pure returns (Schema) { - if (_schema.length > MAX_TOTAL_FIELDS) revert SchemaLib_InvalidLength(_schema.length); + function encode(SchemaType[] memory schemas) internal pure returns (Schema) { + if (schemas.length > MAX_TOTAL_FIELDS) revert SchemaLib_InvalidLength(schemas.length); uint256 schema; uint256 totalLength; uint256 dynamicFields; // Compute the length of the schema and the number of static fields // and store the schema types in the encoded schema - for (uint256 i = 0; i < _schema.length; ) { - uint256 staticByteLength = _schema[i].getStaticByteLength(); + for (uint256 i = 0; i < schemas.length; ) { + uint256 staticByteLength = schemas[i].getStaticByteLength(); if (staticByteLength == 0) { // Increase the dynamic field count if the field is dynamic @@ -59,7 +59,7 @@ library SchemaLib { totalLength += staticByteLength; // Sequentially store schema types after the first 4 bytes (which are reserved for length and field numbers) // (safe because of the initial _schema.length check) - schema |= uint256(_schema[i]) << ((WORD_LAST_INDEX - 4 - i) * BYTE_TO_BITS); + schema |= uint256(schemas[i]) << ((WORD_LAST_INDEX - 4 - i) * BYTE_TO_BITS); i++; } } @@ -70,7 +70,7 @@ library SchemaLib { // Get the static field count uint256 staticFields; unchecked { - staticFields = _schema.length - dynamicFields; + staticFields = schemas.length - dynamicFields; } // Store total static length in the first 2 bytes, @@ -168,40 +168,34 @@ library SchemaInstance { uint256 _numTotalFields = _numStaticFields + _numDynamicFields; if (_numTotalFields > MAX_TOTAL_FIELDS) revert SchemaLib.SchemaLib_InvalidLength(_numTotalFields); - // No static field can be after a dynamic field - uint256 countStaticFields; - uint256 countDynamicFields; + // No dynamic field can be before a dynamic field uint256 _staticDataLength; - for (uint256 i; i < _numTotalFields; ) { + for (uint256 i; i < _numStaticFields; ) { uint256 staticByteLength = schema.atIndex(i).getStaticByteLength(); - if (staticByteLength > 0) { - _staticDataLength += staticByteLength; - // Static field in dynamic part - if (i >= _numStaticFields) revert SchemaLib.SchemaLib_StaticTypeAfterDynamicType(); - unchecked { - countStaticFields++; - } - } else { - // Dynamic field in static part - if (i < _numStaticFields) revert SchemaLib.SchemaLib_StaticTypeAfterDynamicType(); - unchecked { - countDynamicFields++; - } + if (staticByteLength == 0) { + revert SchemaLib.SchemaLib_StaticTypeAfterDynamicType(); } + _staticDataLength += staticByteLength; unchecked { i++; } } + // Static length sums must match if (_staticDataLength != schema.staticDataLength()) { revert SchemaLib.SchemaLib_InvalidLength(schema.staticDataLength()); } - // Number of static fields must match - if (countStaticFields != _numStaticFields) revert SchemaLib.SchemaLib_InvalidLength(countStaticFields); - - // Number of dynamic fields must match - if (countDynamicFields != _numDynamicFields) revert SchemaLib.SchemaLib_InvalidLength(countDynamicFields); + // No static field can be after a dynamic field + for (uint256 i = _numStaticFields; i < _numTotalFields; ) { + uint256 staticByteLength = schema.atIndex(i).getStaticByteLength(); + if (staticByteLength > 0) { + revert SchemaLib.SchemaLib_StaticTypeAfterDynamicType(); + } + unchecked { + i++; + } + } } /** diff --git a/packages/store/src/Slice.sol b/packages/store/src/Slice.sol index 65f1112c1e..0a0571c867 100644 --- a/packages/store/src/Slice.sol +++ b/packages/store/src/Slice.sol @@ -27,13 +27,13 @@ library SliceLib { * @return A new Slice representing the bytes array */ function fromBytes(bytes memory data) internal pure returns (Slice) { - uint256 _pointer; + uint256 pointer; assembly { - _pointer := add(data, 0x20) // pointer to first data byte + pointer := add(data, 0x20) // pointer to first data byte } // Pointer is stored in upper 128 bits, length is stored in lower 128 bits - return Slice.wrap((_pointer << 128) | (data.length & MASK_LEN)); + return Slice.wrap((pointer << 128) | (data.length & MASK_LEN)); } /** @@ -56,18 +56,18 @@ library SliceLib { */ function getSubslice(bytes memory data, uint256 start, uint256 end) internal pure returns (Slice) { // TODO this check helps catch bugs and can eventually be removed - if (!(start <= end && end <= data.length)) revert Slice_OutOfBounds(data, start, end); + if (start > end || end > data.length) revert Slice_OutOfBounds(data, start, end); - uint256 _pointer; + uint256 pointer; assembly { - _pointer := add(data, 0x20) // pointer to first data byte + pointer := add(data, 0x20) // pointer to first data byte } - _pointer += start; + pointer += start; uint256 _len = end - start; // Pointer is stored in upper 128 bits, length is stored in lower 128 bits - return Slice.wrap((_pointer << 128) | (_len & MASK_LEN)); + return Slice.wrap((pointer << 128) | (_len & MASK_LEN)); } } @@ -107,7 +107,7 @@ library SliceInstance { data = new bytes(_length); uint256 toPointer; assembly { - toPointer := add(data, 32) + toPointer := add(data, 0x20) } // Copy the slice contents to the array Memory.copy(fromPointer, toPointer, _length); @@ -120,7 +120,7 @@ library SliceInstance { * @return result The bytes32 representation of the provided Slice. */ function toBytes32(Slice self) internal pure returns (bytes32 result) { - uint256 memoryPointer = self.pointer(); + uint256 memoryPointer = pointer(self); /// @solidity memory-safe-assembly assembly { result := mload(memoryPointer) diff --git a/packages/store/src/Storage.sol b/packages/store/src/Storage.sol index a1cf0cde72..c96fc9fcc8 100644 --- a/packages/store/src/Storage.sol +++ b/packages/store/src/Storage.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.21; -import { leftMask } from "./leftMask.sol"; +import { rightMask } from "./rightMask.sol"; import { Memory } from "./Memory.sol"; import { BYTE_TO_BITS } from "./constants.sol"; @@ -57,7 +57,7 @@ library Storage { wordRemainder = 32 - offset; } - uint256 mask = leftMask(length); + uint256 mask = ~rightMask(length); /// @solidity memory-safe-assembly assembly { // Load data from memory and offset it to match storage @@ -103,16 +103,16 @@ library Storage { // For the last partial word, apply a mask to the end if (length > 0) { - uint256 mask = leftMask(length); + uint256 mask = rightMask(length); /// @solidity memory-safe-assembly assembly { sstore( storagePointer, or( // store the left part - and(mload(memoryPointer), mask), + and(mload(memoryPointer), not(mask)), // preserve the right part - and(sload(storagePointer), not(mask)) + and(sload(storagePointer), mask) ) ) } @@ -160,7 +160,7 @@ library Storage { assembly { // Solidity's YulUtilFunctions::roundUpFunction function round_up_to_mul_of_32(value) -> _result { - _result := and(add(value, 31), not(31)) + _result := and(add(value, 0x1F), not(0x1F)) } // Allocate memory @@ -202,9 +202,9 @@ library Storage { uint256 mask; if (length < wordRemainder) { - mask = leftMask(length); + mask = rightMask(length); } else { - mask = leftMask(wordRemainder); + mask = rightMask(wordRemainder); } /// @solidity memory-safe-assembly assembly { @@ -215,9 +215,9 @@ library Storage { memoryPointer, or( // store the left part - and(offsetData, mask), + and(offsetData, not(mask)), // preserve the right parts - and(mload(memoryPointer), not(mask)) + and(mload(memoryPointer), mask) ) ) } @@ -249,16 +249,16 @@ library Storage { // For the last partial word, apply a mask to the end if (length > 0) { - uint256 mask = leftMask(length); + uint256 mask = rightMask(length); /// @solidity memory-safe-assembly assembly { mstore( memoryPointer, or( // store the left part - and(sload(storagePointer), mask), + and(sload(storagePointer), not(mask)), // preserve the right part - and(mload(memoryPointer), not(mask)) + and(mload(memoryPointer), mask) ) ) } diff --git a/packages/store/src/StoreCore.sol b/packages/store/src/StoreCore.sol index 988004b94e..d3ba40ec7b 100644 --- a/packages/store/src/StoreCore.sol +++ b/packages/store/src/StoreCore.sol @@ -16,7 +16,7 @@ import { IStoreHook } from "./IStoreHook.sol"; import { StoreSwitch } from "./StoreSwitch.sol"; import { Hook, HookLib } from "./Hook.sol"; import { BEFORE_SET_RECORD, AFTER_SET_RECORD, BEFORE_SPLICE_STATIC_DATA, AFTER_SPLICE_STATIC_DATA, BEFORE_SPLICE_DYNAMIC_DATA, AFTER_SPLICE_DYNAMIC_DATA, BEFORE_DELETE_RECORD, AFTER_DELETE_RECORD } from "./storeHookTypes.sol"; -import { ResourceId, ResourceIdInstance } from "./ResourceId.sol"; +import { ResourceId } from "./ResourceId.sol"; import { RESOURCE_TABLE, RESOURCE_OFFCHAIN_TABLE } from "./storeResourceTypes.sol"; /** @@ -24,7 +24,6 @@ import { RESOURCE_TABLE, RESOURCE_OFFCHAIN_TABLE } from "./storeResourceTypes.so * @notice This library includes implementations for all IStore methods and events related to the store actions. */ library StoreCore { - using ResourceIdInstance for ResourceId; /** * @notice Emitted when a new record is set in the store. * @param tableId The ID of the table where the record is set. @@ -961,8 +960,6 @@ library StoreCore { * They are not intended to be used directly by consumers of StoreCore. */ library StoreCoreInternal { - using ResourceIdInstance for ResourceId; - bytes32 internal constant SLOT = keccak256("mud.store"); bytes32 internal constant DYNAMIC_DATA_SLOT = keccak256("mud.store.dynamicData"); bytes32 internal constant DYNAMIC_DATA_LENGTH_SLOT = keccak256("mud.store.dynamicDataLength"); diff --git a/packages/store/src/StoreHook.sol b/packages/store/src/StoreHook.sol index 4a927c039e..b423d6fff2 100644 --- a/packages/store/src/StoreHook.sol +++ b/packages/store/src/StoreHook.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.21; -import { IStoreHook, STORE_HOOK_INTERFACE_ID } from "./IStoreHook.sol"; -import { ERC165_INTERFACE_ID } from "./IERC165.sol"; +import { IStoreHook } from "./IStoreHook.sol"; +import { IERC165 } from "./IERC165.sol"; import { PackedCounter } from "./PackedCounter.sol"; import { FieldLayout } from "./FieldLayout.sol"; import { ResourceId } from "./ResourceId.sol"; @@ -21,7 +21,7 @@ abstract contract StoreHook is IStoreHook { * @return true if the interface is supported, false otherwise. */ function supportsInterface(bytes4 interfaceId) public pure virtual returns (bool) { - return interfaceId == STORE_HOOK_INTERFACE_ID || interfaceId == ERC165_INTERFACE_ID; + return interfaceId == type(IStoreHook).interfaceId || interfaceId == type(IERC165).interfaceId; } /** diff --git a/packages/store/src/leftMask.sol b/packages/store/src/leftMask.sol deleted file mode 100644 index 9343cfc5a8..0000000000 --- a/packages/store/src/leftMask.sol +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.8.21; - -import { BYTE_TO_BITS } from "./constants.sol"; - -/** - * @title Byte Mask Utility - * @notice Utility functions to manage bytes in memory. - * @dev Adapted from https://github.com/dk1a/solidity-stringutils/blob/main/src/utils/mem.sol#L149-L167 - */ - -/** - * @notice Computes a left-aligned byte mask based on the provided byte length. - * @dev The mask is used to extract a specified number of leftmost bytes. - * For byte lengths greater than or equal to 32, it returns the max value of type(uint256). - * Examples: - * length 0: 0x000000...000000 - * length 1: 0xff0000...000000 - * length 2: 0xffff00...000000 - * ... - * length 30: 0xffffff...ff0000 - * length 31: 0xffffff...ffff00 - * length 32+: 0xffffff...ffffff - * @param byteLength The number of leftmost bytes to be masked. - * @return mask A left-aligned byte mask corresponding to the specified byte length. - */ -function leftMask(uint256 byteLength) pure returns (uint256 mask) { - unchecked { - return ~(type(uint256).max >> (byteLength * BYTE_TO_BITS)); - } -} diff --git a/packages/store/src/rightMask.sol b/packages/store/src/rightMask.sol new file mode 100644 index 0000000000..932b031dc9 --- /dev/null +++ b/packages/store/src/rightMask.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.21; + +import { BYTE_TO_BITS } from "./constants.sol"; + +/** + * @title Byte Mask Utility + * @notice Utility functions to manage bytes in memory. + * @dev Adapted from https://github.com/dk1a/solidity-stringutils/blob/main/src/utils/mem.sol#L149-L167 + */ + +/** + * @notice Computes a right-aligned byte mask based on the provided byte length. + * @dev The mask is used to extract a specified number of rightmost bytes. + + * @param byteLength The number of rightmost bytes to be masked. + * @return mask A right-aligned byte mask corresponding to the specified byte length. + */ +function rightMask(uint256 byteLength) pure returns (uint256 mask) { + unchecked { + return type(uint256).max >> (byteLength * BYTE_TO_BITS); + } +} diff --git a/packages/store/src/tightcoder/TightCoder.sol b/packages/store/src/tightcoder/TightCoder.sol index 320792cbed..5da9e8899e 100644 --- a/packages/store/src/tightcoder/TightCoder.sol +++ b/packages/store/src/tightcoder/TightCoder.sol @@ -30,7 +30,7 @@ library TightCoder { assembly { // Solidity's YulUtilFunctions::roundUpFunction function round_up_to_mul_of_32(value) -> _result { - _result := and(add(value, 31), not(31)) + _result := and(add(value, 0x1F), not(0x1F)) } // Allocate memory @@ -84,7 +84,7 @@ library TightCoder { // Allocate memory array := mload(0x40) let arrayPointer := add(array, 0x20) - mstore(0x40, add(arrayPointer, mul(arrayLength, 32))) + mstore(0x40, add(arrayPointer, mul(arrayLength, 0x20))) // Store length mstore(array, arrayLength) diff --git a/packages/store/test/ResourceId.t.sol b/packages/store/test/ResourceId.t.sol index 0fa445f7df..e7c731c579 100644 --- a/packages/store/test/ResourceId.t.sol +++ b/packages/store/test/ResourceId.t.sol @@ -5,11 +5,9 @@ import { Test, console } from "forge-std/Test.sol"; import { GasReporter } from "@latticexyz/gas-report/src/GasReporter.sol"; import { ResourceId } from "../src/ResourceId.sol"; import { RESOURCE_TABLE, RESOURCE_OFFCHAIN_TABLE } from "../src/storeResourceTypes.sol"; -import { ResourceId, ResourceIdInstance, ResourceIdLib } from "../src/ResourceId.sol"; +import { ResourceId, ResourceIdLib } from "../src/ResourceId.sol"; contract ResourceIdTest is Test, GasReporter { - using ResourceIdInstance for ResourceId; - function testEncode() public { startGasReport("encode table ID with name and type"); ResourceId tableId = ResourceIdLib.encode({ typeId: RESOURCE_TABLE, name: "name" }); diff --git a/packages/store/test/Schema.t.sol b/packages/store/test/Schema.t.sol index e10157635d..f22abad9e9 100644 --- a/packages/store/test/Schema.t.sol +++ b/packages/store/test/Schema.t.sol @@ -6,6 +6,58 @@ import { GasReporter } from "@latticexyz/gas-report/src/GasReporter.sol"; import { SchemaType } from "@latticexyz/schema-type/src/solidity/SchemaType.sol"; import { Schema, SchemaLib } from "../src/Schema.sol"; import { SchemaEncodeHelper } from "./SchemaEncodeHelper.sol"; +import { WORD_LAST_INDEX, BYTE_TO_BITS, LayoutOffsets } from "../src/constants.sol"; + +/** + * @notice Encodes a given schema into a single bytes32, without checks. + * @dev Used in testing to create invalid schemas that can be validated seperately. + * @param schemas The list of SchemaTypes that constitute the schema. + * @return The encoded Schema. + */ +function encodeUnsafe(SchemaType[] memory schemas) pure returns (Schema) { + uint256 schema; + uint256 totalLength; + uint256 dynamicFields; + + // Compute the length of the schema and the number of static fields + // and store the schema types in the encoded schema + for (uint256 i = 0; i < schemas.length; ) { + uint256 staticByteLength = schemas[i].getStaticByteLength(); + + if (staticByteLength == 0) { + // Increase the dynamic field count if the field is dynamic + // (safe because of the initial _schema.length check) + unchecked { + dynamicFields++; + } + } + + unchecked { + // (safe because 28 (max _schema.length) * 32 (max static length) < 2**16) + totalLength += staticByteLength; + // Sequentially store schema types after the first 4 bytes (which are reserved for length and field numbers) + // (safe because of the initial _schema.length check) + schema |= uint256(schemas[i]) << ((WORD_LAST_INDEX - 4 - i) * BYTE_TO_BITS); + i++; + } + } + + // Get the static field count + uint256 staticFields; + unchecked { + staticFields = schemas.length - dynamicFields; + } + + // Store total static length in the first 2 bytes, + // number of static fields in the 3rd byte, + // number of dynamic fields in the 4th byte + // (optimizer can handle this, no need for unchecked or single-line assignment) + schema |= totalLength << LayoutOffsets.TOTAL_LENGTH; + schema |= staticFields << LayoutOffsets.NUM_STATIC_FIELDS; + schema |= dynamicFields << LayoutOffsets.NUM_DYNAMIC_FIELDS; + + return Schema.wrap(bytes32(schema)); +} // TODO add tests for all schema types contract SchemaTest is Test, GasReporter { @@ -240,14 +292,51 @@ contract SchemaTest is Test, GasReporter { endGasReport(); } - function testValidateInvalidSchema() public { + function testValidateInvalidLength() public { Schema encodedSchema = Schema.wrap(keccak256("some invalid schema")); vm.expectRevert( abi.encodeWithSelector(SchemaLib.SchemaLib_InvalidLength.selector, encodedSchema.numDynamicFields()) ); - Schema.wrap(keccak256("some invalid schema")).validate({ allowEmpty: false }); + encodedSchema.validate({ allowEmpty: false }); + } + + function testValidateInvalidSchemaStaticAfterDynamic() public { + SchemaType[] memory schema = new SchemaType[](28); + schema[0] = SchemaType.UINT256; + schema[1] = SchemaType.UINT256; + schema[2] = SchemaType.UINT256; + schema[3] = SchemaType.UINT256; + schema[4] = SchemaType.UINT256; + schema[5] = SchemaType.UINT256; + schema[6] = SchemaType.UINT256; + schema[7] = SchemaType.UINT256; + schema[8] = SchemaType.UINT256; + schema[9] = SchemaType.UINT256; + schema[10] = SchemaType.UINT256; + schema[11] = SchemaType.UINT256; + schema[12] = SchemaType.UINT256; + schema[13] = SchemaType.UINT256; + schema[14] = SchemaType.UINT256; + schema[15] = SchemaType.UINT256; + schema[16] = SchemaType.UINT256; + schema[17] = SchemaType.UINT256; + schema[18] = SchemaType.UINT256; + schema[19] = SchemaType.UINT256; + schema[20] = SchemaType.UINT256; + schema[21] = SchemaType.UINT256; + schema[22] = SchemaType.UINT32_ARRAY; + schema[23] = SchemaType.UINT256; + schema[24] = SchemaType.UINT32_ARRAY; + schema[25] = SchemaType.UINT32_ARRAY; + schema[26] = SchemaType.UINT32_ARRAY; + schema[27] = SchemaType.UINT32_ARRAY; + Schema encodedSchema = encodeUnsafe(schema); + + vm.expectRevert(SchemaLib.SchemaLib_StaticTypeAfterDynamicType.selector); + + encodedSchema.validate({ allowEmpty: false }); } function testIsEmptyTrue() public { diff --git a/packages/store/test/StoreCore.t.sol b/packages/store/test/StoreCore.t.sol index 73d076ad2b..358a6cde36 100644 --- a/packages/store/test/StoreCore.t.sol +++ b/packages/store/test/StoreCore.t.sol @@ -16,7 +16,7 @@ import { IStore } from "../src/IStore.sol"; import { StoreSwitch } from "../src/StoreSwitch.sol"; import { IStoreHook } from "../src/IStoreHook.sol"; import { Tables, ResourceIds, TablesTableId } from "../src/codegen/index.sol"; -import { ResourceId, ResourceIdLib, ResourceIdInstance } from "../src/ResourceId.sol"; +import { ResourceId, ResourceIdLib } from "../src/ResourceId.sol"; import { RESOURCE_TABLE, RESOURCE_OFFCHAIN_TABLE } from "../src/storeResourceTypes.sol"; import { FieldLayoutEncodeHelper } from "./FieldLayoutEncodeHelper.sol"; import { BEFORE_SET_RECORD, AFTER_SET_RECORD, BEFORE_SPLICE_STATIC_DATA, AFTER_SPLICE_STATIC_DATA, BEFORE_SPLICE_DYNAMIC_DATA, AFTER_SPLICE_DYNAMIC_DATA, BEFORE_DELETE_RECORD, AFTER_DELETE_RECORD, ALL, BEFORE_ALL, AFTER_ALL } from "../src/storeHookTypes.sol"; @@ -34,8 +34,6 @@ struct TestStruct { } contract StoreCoreTest is Test, StoreMock { - using ResourceIdInstance for ResourceId; - TestStruct private testStruct; event HookCalled(bytes); diff --git a/packages/store/test/leftMask.t.sol b/packages/store/test/leftMask.t.sol deleted file mode 100644 index 2b8c5c4e9a..0000000000 --- a/packages/store/test/leftMask.t.sol +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.8.21; - -import { Test } from "forge-std/Test.sol"; -import { GasReporter } from "@latticexyz/gas-report/src/GasReporter.sol"; -import { leftMask } from "../src/leftMask.sol"; - -contract LeftMaskTest is Test, GasReporter { - function testLeftMask() public { - bytes32 mask; - assertEq(leftMask(0), uint256(mask)); - bytes32 highByte = hex"ff"; - for (uint256 i = 1; i <= 32; i++) { - mask |= highByte; - highByte >>= 8; - assertEq(leftMask(i), uint256(mask)); - } - } - - function testFuzzLeftMaskOver32(uint256 byteLength) public { - // for values >32 the mask must always be type(uint256).max - vm.assume(byteLength > 32); - assertEq(leftMask(byteLength), type(uint256).max); - } - - function testLeftMaskOver32() public { - // manually test for overflow issues - for (uint256 i; i < 100; i++) { - assertEq(leftMask(type(uint256).max - i), type(uint256).max); - } - for (uint256 i; i < 100; i++) { - assertEq(leftMask((type(uint256).max >> 8) + 50 - i), type(uint256).max); - } - } -} diff --git a/packages/store/test/rightMask.t.sol b/packages/store/test/rightMask.t.sol new file mode 100644 index 0000000000..d0c2b48ac7 --- /dev/null +++ b/packages/store/test/rightMask.t.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.21; + +import { Test } from "forge-std/Test.sol"; +import { GasReporter } from "@latticexyz/gas-report/src/GasReporter.sol"; +import { rightMask } from "../src/rightMask.sol"; + +contract RightMaskTest is Test, GasReporter { + function testRightMask() public { + bytes32 mask; + assertEq(rightMask(0), type(uint256).max); + bytes32 highByte = hex"ff"; + for (uint256 i = 1; i <= 32; i++) { + mask |= highByte; + highByte >>= 8; + assertEq(rightMask(i), type(uint256).max - uint256(mask)); + } + } + + function testFuzzRightMaskOver32(uint256 byteLength) public { + // for values >32 the mask must always be 0 + vm.assume(byteLength > 32); + assertEq(rightMask(byteLength), 0); + } + + function testRightMaskOver32() public { + // manually test for underflow issues + for (uint256 i; i < 100; i++) { + assertEq(rightMask(type(uint256).max - i), 0); + } + for (uint256 i; i < 100; i++) { + assertEq(rightMask((type(uint256).max >> 8) + 50 - i), 0); + } + } +} diff --git a/packages/world-modules/gas-report.json b/packages/world-modules/gas-report.json index 9fa8b5c85b..d4e7c5f49b 100644 --- a/packages/world-modules/gas-report.json +++ b/packages/world-modules/gas-report.json @@ -3,145 +3,145 @@ "file": "test/ERC20.t.sol", "test": "testApprove", "name": "approve", - "gasUsed": 114369 + "gasUsed": 114348 }, { "file": "test/ERC20.t.sol", "test": "testBurn", "name": "burn", - "gasUsed": 75937 + "gasUsed": 75910 }, { "file": "test/ERC20.t.sol", "test": "testMint", "name": "mint", - "gasUsed": 161776 + "gasUsed": 161749 }, { "file": "test/ERC20.t.sol", "test": "testTransfer", "name": "transfer", - "gasUsed": 93022 + "gasUsed": 92995 }, { "file": "test/ERC20.t.sol", "test": "testTransferFrom", "name": "transferFrom", - "gasUsed": 130367 + "gasUsed": 130331 }, { "file": "test/ERC721.t.sol", "test": "testApproveAllGas", "name": "setApprovalForAll", - "gasUsed": 113996 + "gasUsed": 113972 }, { "file": "test/ERC721.t.sol", "test": "testApproveGas", "name": "approve", - "gasUsed": 88005 + "gasUsed": 87981 }, { "file": "test/ERC721.t.sol", "test": "testBurnGas", "name": "burn", - "gasUsed": 101949 + "gasUsed": 101910 }, { "file": "test/ERC721.t.sol", "test": "testMintGas", "name": "mint", - "gasUsed": 169510 + "gasUsed": 169480 }, { "file": "test/ERC721.t.sol", "test": "testSafeMintToEOAGas", "name": "safeMint", - "gasUsed": 169781 + "gasUsed": 169751 }, { "file": "test/ERC721.t.sol", "test": "testSafeTransferFromToEOAGas", "name": "safeTransferFrom", - "gasUsed": 143789 + "gasUsed": 143744 }, { "file": "test/ERC721.t.sol", "test": "testTransferFromGas", "name": "transferFrom", - "gasUsed": 136946 + "gasUsed": 136901 }, { "file": "test/KeysInTableModule.t.sol", "test": "testInstallComposite", "name": "install keys in table module", - "gasUsed": 1435380 + "gasUsed": 1434753 }, { "file": "test/KeysInTableModule.t.sol", "test": "testInstallGas", "name": "install keys in table module", - "gasUsed": 1435380 + "gasUsed": 1434753 }, { "file": "test/KeysInTableModule.t.sol", "test": "testInstallGas", "name": "set a record on a table with keysInTableModule installed", - "gasUsed": 158854 + "gasUsed": 158830 }, { "file": "test/KeysInTableModule.t.sol", "test": "testInstallSingleton", "name": "install keys in table module", - "gasUsed": 1435380 + "gasUsed": 1434753 }, { "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookCompositeGas", "name": "install keys in table module", - "gasUsed": 1435380 + "gasUsed": 1434753 }, { "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookCompositeGas", "name": "change a composite record on a table with keysInTableModule installed", - "gasUsed": 22492 + "gasUsed": 22483 }, { "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookCompositeGas", "name": "delete a composite record on a table with keysInTableModule installed", - "gasUsed": 155851 + "gasUsed": 155938 }, { "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookGas", "name": "install keys in table module", - "gasUsed": 1435380 + "gasUsed": 1434753 }, { "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookGas", "name": "change a record on a table with keysInTableModule installed", - "gasUsed": 21214 + "gasUsed": 21205 }, { "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookGas", "name": "delete a record on a table with keysInTableModule installed", - "gasUsed": 85049 + "gasUsed": 85160 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testGetKeysWithValueGas", "name": "install keys with value module", - "gasUsed": 681695 + "gasUsed": 681477 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testGetKeysWithValueGas", "name": "Get list of keys with a given value", - "gasUsed": 5665 + "gasUsed": 5659 }, { "file": "test/KeysWithValueModule.t.sol", @@ -153,61 +153,61 @@ "file": "test/KeysWithValueModule.t.sol", "test": "testInstall", "name": "install keys with value module", - "gasUsed": 681695 + "gasUsed": 681477 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testInstall", "name": "set a record on a table with KeysWithValueModule installed", - "gasUsed": 135437 + "gasUsed": 135410 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testSetAndDeleteRecordHook", "name": "install keys with value module", - "gasUsed": 681695 + "gasUsed": 681477 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testSetAndDeleteRecordHook", "name": "change a record on a table with KeysWithValueModule installed", - "gasUsed": 103840 + "gasUsed": 103813 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testSetAndDeleteRecordHook", "name": "delete a record on a table with KeysWithValueModule installed", - "gasUsed": 36489 + "gasUsed": 36468 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testSetField", "name": "install keys with value module", - "gasUsed": 681695 + "gasUsed": 681477 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testSetField", "name": "set a field on a table with KeysWithValueModule installed", - "gasUsed": 146672 + "gasUsed": 146645 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testSetField", "name": "change a field on a table with KeysWithValueModule installed", - "gasUsed": 111431 + "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": 53228 + "gasUsed": 53210 }, { "file": "test/query.t.sol", @@ -225,13 +225,13 @@ "file": "test/query.t.sol", "test": "testCombinedHasValueNotQuery", "name": "CombinedHasValueNotQuery", - "gasUsed": 84679 + "gasUsed": 84673 }, { "file": "test/query.t.sol", "test": "testCombinedHasValueQuery", "name": "CombinedHasValueQuery", - "gasUsed": 15551 + "gasUsed": 15539 }, { "file": "test/query.t.sol", @@ -255,72 +255,72 @@ "file": "test/query.t.sol", "test": "testHasValueQuery", "name": "HasValueQuery", - "gasUsed": 7444 + "gasUsed": 7438 }, { "file": "test/query.t.sol", "test": "testNotValueQuery", "name": "NotValueQuery", - "gasUsed": 46824 + "gasUsed": 46806 }, { "file": "test/StandardDelegationsModule.t.sol", "test": "testCallFromCallboundDelegation", "name": "register a callbound delegation", - "gasUsed": 118138 + "gasUsed": 118105 }, { "file": "test/StandardDelegationsModule.t.sol", "test": "testCallFromCallboundDelegation", "name": "call a system via a callbound delegation", - "gasUsed": 36697 + "gasUsed": 36679 }, { "file": "test/StandardDelegationsModule.t.sol", "test": "testCallFromSystemDelegation", "name": "register a systembound delegation", - "gasUsed": 115691 + "gasUsed": 115658 }, { "file": "test/StandardDelegationsModule.t.sol", "test": "testCallFromSystemDelegation", "name": "call a system via a systembound delegation", - "gasUsed": 33869 + "gasUsed": 33851 }, { "file": "test/StandardDelegationsModule.t.sol", "test": "testCallFromTimeboundDelegation", "name": "register a timebound delegation", - "gasUsed": 112614 + "gasUsed": 112581 }, { "file": "test/StandardDelegationsModule.t.sol", "test": "testCallFromTimeboundDelegation", "name": "call a system via a timebound delegation", - "gasUsed": 26809 + "gasUsed": 26797 }, { "file": "test/UniqueEntityModule.t.sol", "test": "testInstall", "name": "install unique entity module", - "gasUsed": 702723 + "gasUsed": 702511 }, { "file": "test/UniqueEntityModule.t.sol", "test": "testInstall", "name": "get a unique entity nonce (non-root module)", - "gasUsed": 51045 + "gasUsed": 51027 }, { "file": "test/UniqueEntityModule.t.sol", "test": "testInstallRoot", "name": "installRoot unique entity module", - "gasUsed": 671743 + "gasUsed": 671555 }, { "file": "test/UniqueEntityModule.t.sol", "test": "testInstallRoot", "name": "get a unique entity nonce (root module)", - "gasUsed": 51045 + "gasUsed": 51027 } ] diff --git a/packages/world-modules/src/modules/utils/ArrayLib.sol b/packages/world-modules/src/modules/utils/ArrayLib.sol index 0b16c1d44b..8c9f8a2218 100644 --- a/packages/world-modules/src/modules/utils/ArrayLib.sol +++ b/packages/world-modules/src/modules/utils/ArrayLib.sol @@ -36,7 +36,7 @@ library ArrayLib { function filter(bytes32[] memory arr, bytes32 element) internal pure returns (bytes32[] memory) { bytes32[] memory filtered = new bytes32[](arr.length); - uint256 filteredIndex = 0; + uint256 filteredIndex; for (uint256 i; i < arr.length; i++) { if (arr[i] != element) { filtered[filteredIndex] = arr[i]; diff --git a/packages/world-modules/test/KeysWithValueModule.t.sol b/packages/world-modules/test/KeysWithValueModule.t.sol index 1f43c39911..5cb0b3f10d 100644 --- a/packages/world-modules/test/KeysWithValueModule.t.sol +++ b/packages/world-modules/test/KeysWithValueModule.t.sol @@ -9,7 +9,7 @@ import { SchemaType } from "@latticexyz/schema-type/src/solidity/SchemaType.sol" import { StoreSwitch } from "@latticexyz/store/src/StoreSwitch.sol"; import { Schema } from "@latticexyz/store/src/Schema.sol"; import { PackedCounter } from "@latticexyz/store/src/PackedCounter.sol"; -import { ResourceId, ResourceIdInstance } from "@latticexyz/store/src/ResourceId.sol"; +import { ResourceId } from "@latticexyz/store/src/ResourceId.sol"; import { SchemaEncodeHelper } from "@latticexyz/store/test/SchemaEncodeHelper.sol"; import { FieldLayout } from "@latticexyz/store/src/FieldLayout.sol"; import { FieldLayoutEncodeHelper } from "@latticexyz/store/test/FieldLayoutEncodeHelper.sol"; @@ -29,7 +29,6 @@ import { getKeysWithValue } from "../src/modules/keyswithvalue/getKeysWithValue. import { getTargetTableId, MODULE_NAMESPACE_BITS, TABLE_NAMESPACE_BITS } from "../src/modules/keyswithvalue/getTargetTableId.sol"; contract KeysWithValueModuleTest is Test, GasReporter { - using ResourceIdInstance for ResourceId; using WorldResourceIdInstance for ResourceId; IBaseWorld world; diff --git a/packages/world-modules/test/PuppetModule.t.sol b/packages/world-modules/test/PuppetModule.t.sol index 6a7383ab3e..be555ad215 100644 --- a/packages/world-modules/test/PuppetModule.t.sol +++ b/packages/world-modules/test/PuppetModule.t.sol @@ -11,7 +11,7 @@ import { RESOURCE_SYSTEM } from "@latticexyz/world/src/worldResourceTypes.sol"; import { IBaseWorld } from "@latticexyz/world/src/codegen/interfaces/IBaseWorld.sol"; import { IWorldErrors } from "@latticexyz/world/src/IWorldErrors.sol"; -import { DELEGATION_CONTROL_INTERFACE_ID } from "@latticexyz/world/src/IDelegationControl.sol"; +import { IDelegationControl } from "@latticexyz/world/src/IDelegationControl.sol"; import { createCoreModule } from "@latticexyz/world/test/createCoreModule.sol"; import { Systems } from "@latticexyz/world/src/codegen/tables/Systems.sol"; diff --git a/packages/world-modules/test/StandardDelegationsModule.t.sol b/packages/world-modules/test/StandardDelegationsModule.t.sol index 8422838268..b70728e9bb 100644 --- a/packages/world-modules/test/StandardDelegationsModule.t.sol +++ b/packages/world-modules/test/StandardDelegationsModule.t.sol @@ -11,7 +11,7 @@ import { RESOURCE_SYSTEM } from "@latticexyz/world/src/worldResourceTypes.sol"; import { IBaseWorld } from "@latticexyz/world/src/codegen/interfaces/IBaseWorld.sol"; import { IWorldErrors } from "@latticexyz/world/src/IWorldErrors.sol"; -import { DELEGATION_CONTROL_INTERFACE_ID } from "@latticexyz/world/src/IDelegationControl.sol"; +import { IDelegationControl } from "@latticexyz/world/src/IDelegationControl.sol"; import { createCoreModule } from "@latticexyz/world/test/createCoreModule.sol"; import { Systems } from "@latticexyz/world/src/codegen/tables/Systems.sol"; @@ -162,7 +162,7 @@ contract StandardDelegationsModuleTest is Test, GasReporter { abi.encodeWithSelector( IWorldErrors.World_InterfaceNotSupported.selector, address(noDelegationControlSystem), - DELEGATION_CONTROL_INTERFACE_ID + type(IDelegationControl).interfaceId ) ); world.registerDelegation(delegatee, noDelegationControlId, new bytes(1)); diff --git a/packages/world/gas-report.json b/packages/world/gas-report.json index a3e650f9d0..894968a314 100644 --- a/packages/world/gas-report.json +++ b/packages/world/gas-report.json @@ -45,133 +45,133 @@ "file": "test/BatchCall.t.sol", "test": "testBatchCallFromReturnData", "name": "call systems with batchCallFrom", - "gasUsed": 52561 + "gasUsed": 52559 }, { "file": "test/BatchCall.t.sol", "test": "testBatchCallReturnData", "name": "call systems with batchCall", - "gasUsed": 51407 + "gasUsed": 51405 }, { "file": "test/Factories.t.sol", "test": "testCreate2Factory", "name": "deploy contract via Create2", - "gasUsed": 4676449 + "gasUsed": 4659744 }, { "file": "test/Factories.t.sol", "test": "testWorldFactory", "name": "deploy world via WorldFactory", - "gasUsed": 12514303 + "gasUsed": 12509892 }, { "file": "test/World.t.sol", "test": "testCall", "name": "call a system via the World", - "gasUsed": 12370 + "gasUsed": 12361 }, { "file": "test/World.t.sol", "test": "testCallFromNamespaceDelegation", "name": "call a system via a namespace fallback delegation", - "gasUsed": 26143 + "gasUsed": 26131 }, { "file": "test/World.t.sol", "test": "testCallFromUnlimitedDelegation", "name": "register an unlimited delegation", - "gasUsed": 47532 + "gasUsed": 47514 }, { "file": "test/World.t.sol", "test": "testCallFromUnlimitedDelegation", "name": "call a system via an unlimited delegation", - "gasUsed": 12806 + "gasUsed": 12797 }, { "file": "test/World.t.sol", "test": "testDeleteRecord", "name": "Delete record", - "gasUsed": 9846 + "gasUsed": 9837 }, { "file": "test/World.t.sol", "test": "testPushToDynamicField", "name": "Push data to the table", - "gasUsed": 85842 + "gasUsed": 85833 }, { "file": "test/World.t.sol", "test": "testRegisterFunctionSelector", "name": "Register a function selector", - "gasUsed": 83123 + "gasUsed": 83102 }, { "file": "test/World.t.sol", "test": "testRegisterNamespace", "name": "Register a new namespace", - "gasUsed": 120918 + "gasUsed": 120879 }, { "file": "test/World.t.sol", "test": "testRegisterRootFunctionSelector", "name": "Register a root function selector", - "gasUsed": 80424 + "gasUsed": 80403 }, { "file": "test/World.t.sol", "test": "testRegisterSystem", "name": "register a system", - "gasUsed": 164348 + "gasUsed": 164303 }, { "file": "test/World.t.sol", "test": "testRegisterTable", "name": "Register a new table in the namespace", - "gasUsed": 537060 + "gasUsed": 536805 }, { "file": "test/World.t.sol", "test": "testSetField", "name": "Write data to a table field", - "gasUsed": 36912 + "gasUsed": 36903 }, { "file": "test/World.t.sol", "test": "testSetRecord", "name": "Write data to the table", - "gasUsed": 39056 + "gasUsed": 39047 }, { "file": "test/World.t.sol", "test": "testUnregisterUnlimitedDelegation", "name": "unregister an unlimited delegation", - "gasUsed": 26191 + "gasUsed": 26173 }, { "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": 25990 + "gasUsed": 25981 }, { "file": "test/WorldDynamicUpdate.t.sol", "test": "testSpliceDynamicData", "name": "update in field 1 item (warm)", - "gasUsed": 13191 + "gasUsed": 13182 }, { "file": "test/WorldResourceId.t.sol", diff --git a/packages/world/src/Create2.sol b/packages/world/src/Create2.sol index 486dadb5c2..58678995f3 100644 --- a/packages/world/src/Create2.sol +++ b/packages/world/src/Create2.sol @@ -29,7 +29,8 @@ library Create2 { // code for the constructor. So the code starts at creationCode+0x20, and is mload(creationCode) // bytes long. addr := create2(0, add(creationCode, 0x20), mload(creationCode), salt) - if iszero(extcodesize(addr)) { + // If the create2 call failed, then the address will be zero + if iszero(addr) { revert(0, 0) } } diff --git a/packages/world/src/DelegationControl.sol b/packages/world/src/DelegationControl.sol index eb79e55f1e..e1df8110b2 100644 --- a/packages/world/src/DelegationControl.sol +++ b/packages/world/src/DelegationControl.sol @@ -1,11 +1,11 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.21; -import { WorldContextConsumer } from "./WorldContext.sol"; import { System } from "./System.sol"; -import { IDelegationControl, DELEGATION_CONTROL_INTERFACE_ID } from "./IDelegationControl.sol"; -import { WORLD_CONTEXT_CONSUMER_INTERFACE_ID } from "./IWorldContextConsumer.sol"; -import { IERC165, ERC165_INTERFACE_ID } from "./IERC165.sol"; +import { WorldContextConsumer } from "./WorldContext.sol"; +import { IDelegationControl } from "./IDelegationControl.sol"; +import { IWorldContextConsumer } from "./IWorldContextConsumer.sol"; +import { IERC165 } from "./IERC165.sol"; /** * @title DelegationControl @@ -23,8 +23,8 @@ abstract contract DelegationControl is System, IDelegationControl { bytes4 interfaceId ) public pure virtual override(IERC165, WorldContextConsumer) returns (bool) { return - interfaceId == DELEGATION_CONTROL_INTERFACE_ID || - interfaceId == WORLD_CONTEXT_CONSUMER_INTERFACE_ID || - interfaceId == ERC165_INTERFACE_ID; + interfaceId == type(IDelegationControl).interfaceId || + interfaceId == type(IWorldContextConsumer).interfaceId || + interfaceId == type(IERC165).interfaceId; } } diff --git a/packages/world/src/IDelegationControl.sol b/packages/world/src/IDelegationControl.sol index b17be98041..c2ee95abb6 100644 --- a/packages/world/src/IDelegationControl.sol +++ b/packages/world/src/IDelegationControl.sol @@ -1,16 +1,9 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.21; -import { IWorldContextConsumer, WORLD_CONTEXT_CONSUMER_INTERFACE_ID } from "./IWorldContextConsumer.sol"; +import { IWorldContextConsumer } from "./IWorldContextConsumer.sol"; import { ResourceId } from "./WorldResourceId.sol"; -/** - * @dev Calculation for ERC-165 interface ID for the IDelegationControl interface. - * Combines the selector of the `verify` function with the interface ID of IWorldContextConsumer. - */ -bytes4 constant DELEGATION_CONTROL_INTERFACE_ID = IDelegationControl.verify.selector ^ - WORLD_CONTEXT_CONSUMER_INTERFACE_ID; - /** * @title IDelegationControl * @dev Interface for managing and verifying delegations within the context of a world. diff --git a/packages/world/src/IERC165.sol b/packages/world/src/IERC165.sol index 61f50a6dda..798e6278e3 100644 --- a/packages/world/src/IERC165.sol +++ b/packages/world/src/IERC165.sol @@ -1,11 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.21; -/** - * @dev Calculation for ERC-165 interface ID for the `supportsInterface` function. - */ -bytes4 constant ERC165_INTERFACE_ID = IERC165.supportsInterface.selector; - /** * @title IERC165 * @dev Interface for the ERC-165 standard as described in the EIP-165. diff --git a/packages/world/src/IModule.sol b/packages/world/src/IModule.sol index 14bb456fee..9e8b4eff52 100644 --- a/packages/world/src/IModule.sol +++ b/packages/world/src/IModule.sol @@ -3,14 +3,6 @@ pragma solidity >=0.8.21; import { IERC165 } from "./IERC165.sol"; -/** - * @dev Calculation for ERC-165 interface ID for the IModule functions. - * See: https://eips.ethereum.org/EIPS/eip-165 - */ -bytes4 constant MODULE_INTERFACE_ID = IModule.getName.selector ^ - IModule.installRoot.selector ^ - IModule.install.selector; - /** * @title IModule * @dev Interface for the Module system. diff --git a/packages/world/src/ISystemHook.sol b/packages/world/src/ISystemHook.sol index c09911904c..7c7940383a 100644 --- a/packages/world/src/ISystemHook.sol +++ b/packages/world/src/ISystemHook.sol @@ -4,13 +4,6 @@ pragma solidity >=0.8.21; import { IERC165 } from "./IERC165.sol"; import { ResourceId } from "./WorldResourceId.sol"; -/** - * @dev Calculation for ERC-165 interface ID for the ISystemHook functions. - * See: https://eips.ethereum.org/EIPS/eip-165 - */ -bytes4 constant SYSTEM_HOOK_INTERFACE_ID = ISystemHook.onBeforeCallSystem.selector ^ - ISystemHook.onAfterCallSystem.selector; - /** * @title ISystemHook * @dev Interface defining system hooks for external functionality. diff --git a/packages/world/src/IWorldContextConsumer.sol b/packages/world/src/IWorldContextConsumer.sol index 04eb86ea78..9ea1403016 100644 --- a/packages/world/src/IWorldContextConsumer.sol +++ b/packages/world/src/IWorldContextConsumer.sol @@ -10,10 +10,6 @@ import { IERC165 } from "./IERC165.sol"; * Additionally, it integrates with the ERC-165 standard for interface detection. */ -bytes4 constant WORLD_CONTEXT_CONSUMER_INTERFACE_ID = IWorldContextConsumer._msgSender.selector ^ - IWorldContextConsumer._msgValue.selector ^ - IWorldContextConsumer._world.selector; - /** * @title WorldContextConsumer - Extracting trusted context values from appended calldata. * @notice This contract is designed to extract trusted context values (like msg.sender and msg.value) diff --git a/packages/world/src/Module.sol b/packages/world/src/Module.sol index b348af4b15..bbc795ce52 100644 --- a/packages/world/src/Module.sol +++ b/packages/world/src/Module.sol @@ -2,9 +2,9 @@ pragma solidity >=0.8.21; import { WorldContextConsumer } from "./WorldContext.sol"; -import { WORLD_CONTEXT_CONSUMER_INTERFACE_ID } from "./IWorldContextConsumer.sol"; -import { IModule, MODULE_INTERFACE_ID } from "./IModule.sol"; -import { IERC165, ERC165_INTERFACE_ID } from "./IERC165.sol"; +import { IWorldContextConsumer } from "./IWorldContextConsumer.sol"; +import { IModule, IModule } from "./IModule.sol"; +import { IERC165 } from "./IERC165.sol"; import { InstalledModules } from "./codegen/tables/InstalledModules.sol"; /** @@ -22,9 +22,9 @@ abstract contract Module is IModule, WorldContextConsumer { bytes4 interfaceId ) public pure virtual override(IERC165, WorldContextConsumer) returns (bool) { return - interfaceId == MODULE_INTERFACE_ID || - interfaceId == WORLD_CONTEXT_CONSUMER_INTERFACE_ID || - interfaceId == ERC165_INTERFACE_ID; + interfaceId == type(IModule).interfaceId || + interfaceId == type(IWorldContextConsumer).interfaceId || + interfaceId == type(IERC165).interfaceId; } /** diff --git a/packages/world/src/SystemHook.sol b/packages/world/src/SystemHook.sol index 3e360438c7..50ed4c255e 100644 --- a/packages/world/src/SystemHook.sol +++ b/packages/world/src/SystemHook.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.21; -import { ISystemHook, SYSTEM_HOOK_INTERFACE_ID } from "./ISystemHook.sol"; -import { ERC165_INTERFACE_ID } from "./IERC165.sol"; +import { ISystemHook } from "./ISystemHook.sol"; +import { IERC165 } from "./IERC165.sol"; /** * @title SystemHook @@ -17,6 +17,6 @@ abstract contract SystemHook is ISystemHook { * @return true if the contract implements `interfaceId`, false otherwise. */ function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) { - return interfaceId == SYSTEM_HOOK_INTERFACE_ID || interfaceId == ERC165_INTERFACE_ID; + return interfaceId == type(ISystemHook).interfaceId || interfaceId == type(IERC165).interfaceId; } } diff --git a/packages/world/src/World.sol b/packages/world/src/World.sol index 20284534c1..6ddad55db0 100644 --- a/packages/world/src/World.sol +++ b/packages/world/src/World.sol @@ -20,7 +20,7 @@ import { InstalledModules } from "./codegen/tables/InstalledModules.sol"; import { UserDelegationControl } from "./codegen/tables/UserDelegationControl.sol"; import { NamespaceDelegationControl } from "./codegen/tables/NamespaceDelegationControl.sol"; -import { IModule, MODULE_INTERFACE_ID } from "./IModule.sol"; +import { IModule, IModule } from "./IModule.sol"; import { IWorldKernel } from "./IWorldKernel.sol"; import { FunctionSelectors } from "./codegen/tables/FunctionSelectors.sol"; @@ -97,7 +97,7 @@ contract World is StoreData, IWorldKernel { */ function _installRootModule(IModule module, bytes memory args) internal { // Require the provided address to implement the IModule interface - requireInterface(address(module), MODULE_INTERFACE_ID); + requireInterface(address(module), type(IModule).interfaceId); WorldContextProviderLib.delegatecallWithContextOrRevert({ msgSender: msg.sender, diff --git a/packages/world/src/WorldContext.sol b/packages/world/src/WorldContext.sol index d5e6f3a723..3d4dc78dd1 100644 --- a/packages/world/src/WorldContext.sol +++ b/packages/world/src/WorldContext.sol @@ -3,8 +3,8 @@ pragma solidity >=0.8.21; import { StoreSwitch } from "@latticexyz/store/src/StoreSwitch.sol"; import { revertWithBytes } from "./revertWithBytes.sol"; -import { ERC165_INTERFACE_ID } from "./IERC165.sol"; -import { IWorldContextConsumer, WORLD_CONTEXT_CONSUMER_INTERFACE_ID } from "./IWorldContextConsumer.sol"; +import { IERC165 } from "./IERC165.sol"; +import { IWorldContextConsumer } from "./IWorldContextConsumer.sol"; // The context size is 20 bytes for msg.sender, and 32 bytes for msg.value uint256 constant CONTEXT_BYTES = 20 + 32; @@ -40,7 +40,7 @@ abstract contract WorldContextConsumer is IWorldContextConsumer { * @return The address of the World contract that routed the call to this WorldContextConsumer. */ function _world() public view returns (address) { - return StoreSwitch.getStoreAddress(); + return WorldContextConsumerLib._world(); } /** @@ -50,7 +50,7 @@ abstract contract WorldContextConsumer is IWorldContextConsumer { * @return True if the interface is supported, false otherwise. */ function supportsInterface(bytes4 interfaceId) public pure virtual returns (bool) { - return interfaceId == WORLD_CONTEXT_CONSUMER_INTERFACE_ID || interfaceId == ERC165_INTERFACE_ID; + return interfaceId == type(IWorldContextConsumer).interfaceId || interfaceId == type(IERC165).interfaceId; } } @@ -65,7 +65,7 @@ library WorldContextConsumerLib { // Load 32 bytes from calldata at position calldatasize() - context size, // then shift left 96 bits (to right-align the address) // 96 = 256 - 20 * 8 - sender := shr(96, calldataload(sub(calldatasize(), CONTEXT_BYTES))) + sender := shr(0x60, calldataload(sub(calldatasize(), CONTEXT_BYTES))) } if (sender == address(0)) sender = msg.sender; } @@ -78,7 +78,7 @@ library WorldContextConsumerLib { function _msgValue() internal pure returns (uint256 value) { assembly { // Load 32 bytes from calldata at position calldatasize() - 32 bytes, - value := calldataload(sub(calldatasize(), 32)) + value := calldataload(sub(calldatasize(), 0x20)) } } diff --git a/packages/world/src/WorldFactory.sol b/packages/world/src/WorldFactory.sol index 763811d606..7706c76fe7 100644 --- a/packages/world/src/WorldFactory.sol +++ b/packages/world/src/WorldFactory.sol @@ -15,7 +15,7 @@ import { ROOT_NAMESPACE_ID } from "./constants.sol"; */ contract WorldFactory is IWorldFactory { /// @notice Address of the core module to be set in the World instances. - IModule public coreModule; + IModule public immutable coreModule; /// @notice Counter to keep track of the number of World instances deployed. uint256 public worldCount; diff --git a/packages/world/src/codegen/interfaces/IWorldRegistrationSystem.sol b/packages/world/src/codegen/interfaces/IWorldRegistrationSystem.sol index 2c889779b7..95523a733e 100644 --- a/packages/world/src/codegen/interfaces/IWorldRegistrationSystem.sol +++ b/packages/world/src/codegen/interfaces/IWorldRegistrationSystem.sol @@ -5,7 +5,7 @@ pragma solidity >=0.8.21; import { ResourceId } from "@latticexyz/store/src/ResourceId.sol"; import { ISystemHook } from "./../../ISystemHook.sol"; -import { WorldContextConsumer } from "./../../WorldContext.sol"; +import { System } from "./../../System.sol"; /** * @title IWorldRegistrationSystem @@ -18,7 +18,7 @@ interface IWorldRegistrationSystem { function unregisterSystemHook(ResourceId systemId, ISystemHook hookAddress) external; - function registerSystem(ResourceId systemId, WorldContextConsumer system, bool publicAccess) external; + function registerSystem(ResourceId systemId, System system, bool publicAccess) external; function registerFunctionSelector( ResourceId systemId, diff --git a/packages/world/src/modules/core/CoreModule.sol b/packages/world/src/modules/core/CoreModule.sol index 9a2d1979a5..83a91af3e8 100644 --- a/packages/world/src/modules/core/CoreModule.sol +++ b/packages/world/src/modules/core/CoreModule.sol @@ -1,7 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.21; -import { WorldContextProviderLib, WorldContextConsumer } from "../../WorldContext.sol"; +import { System } from "../../System.sol"; +import { WorldContextProviderLib } from "../../WorldContext.sol"; import { ROOT_NAMESPACE_ID, STORE_NAMESPACE_ID, WORLD_NAMESPACE_ID } from "../../constants.sol"; import { Module } from "../../Module.sol"; @@ -131,7 +132,7 @@ contract CoreModule is Module { msgSender: _msgSender(), msgValue: 0, target: coreRegistrationSystem, - callData: abi.encodeCall(WorldRegistrationSystem.registerSystem, (systemId, WorldContextConsumer(target), true)) + callData: abi.encodeCall(WorldRegistrationSystem.registerSystem, (systemId, System(target), true)) }); } diff --git a/packages/world/src/modules/core/implementations/BalanceTransferSystem.sol b/packages/world/src/modules/core/implementations/BalanceTransferSystem.sol index 53b7c88fc0..491f9d7b57 100644 --- a/packages/world/src/modules/core/implementations/BalanceTransferSystem.sol +++ b/packages/world/src/modules/core/implementations/BalanceTransferSystem.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.21; -import { ResourceId, ResourceIdInstance } from "@latticexyz/store/src/ResourceId.sol"; -import { ResourceIds } from "@latticexyz/store/src/codegen/tables/ResourceIds.sol"; +import { ResourceId } from "@latticexyz/store/src/ResourceId.sol"; import { System } from "../../../System.sol"; import { revertWithBytes } from "../../../revertWithBytes.sol"; @@ -18,7 +17,6 @@ import { Balances } from "../../../codegen/tables/Balances.sol"; * @dev A system contract that facilitates balance transfers in the World and outside of the World. */ contract BalanceTransferSystem is System, IWorldErrors { - using ResourceIdInstance for ResourceId; using WorldResourceIdInstance for ResourceId; /** diff --git a/packages/world/src/modules/core/implementations/ModuleInstallationSystem.sol b/packages/world/src/modules/core/implementations/ModuleInstallationSystem.sol index e5c253f8bd..62a1eb77fc 100644 --- a/packages/world/src/modules/core/implementations/ModuleInstallationSystem.sol +++ b/packages/world/src/modules/core/implementations/ModuleInstallationSystem.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.21; -import { IModule, MODULE_INTERFACE_ID } from "../../../IModule.sol"; +import { IModule } from "../../../IModule.sol"; import { System } from "../../../System.sol"; import { WorldContextProviderLib } from "../../../WorldContext.sol"; import { InstalledModules } from "../../../codegen/tables/InstalledModules.sol"; @@ -21,7 +21,7 @@ contract ModuleInstallationSystem is System { */ function installModule(IModule module, bytes memory args) public { // Require the provided address to implement the IModule interface - requireInterface(address(module), MODULE_INTERFACE_ID); + requireInterface(address(module), type(IModule).interfaceId); WorldContextProviderLib.callWithContextOrRevert({ msgSender: _msgSender(), diff --git a/packages/world/src/modules/core/implementations/StoreRegistrationSystem.sol b/packages/world/src/modules/core/implementations/StoreRegistrationSystem.sol index 379850d5e9..e19dd5f9f8 100644 --- a/packages/world/src/modules/core/implementations/StoreRegistrationSystem.sol +++ b/packages/world/src/modules/core/implementations/StoreRegistrationSystem.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.21; -import { IStoreHook, STORE_HOOK_INTERFACE_ID } from "@latticexyz/store/src/IStoreHook.sol"; +import { IStoreHook } from "@latticexyz/store/src/IStoreHook.sol"; import { StoreCore } from "@latticexyz/store/src/StoreCore.sol"; import { FieldLayout } from "@latticexyz/store/src/FieldLayout.sol"; import { Schema } from "@latticexyz/store/src/Schema.sol"; @@ -71,7 +71,7 @@ contract StoreRegistrationSystem is System, IWorldErrors { function registerStoreHook(ResourceId tableId, IStoreHook hookAddress, uint8 enabledHooksBitmap) public virtual { // Require the hook to implement the store hook interface - requireInterface(address(hookAddress), STORE_HOOK_INTERFACE_ID); + requireInterface(address(hookAddress), type(IStoreHook).interfaceId); // Require caller to own the namespace AccessControl.requireOwner(tableId, _msgSender()); diff --git a/packages/world/src/modules/core/implementations/WorldRegistrationSystem.sol b/packages/world/src/modules/core/implementations/WorldRegistrationSystem.sol index a4fe1f8ad1..1076f8767d 100644 --- a/packages/world/src/modules/core/implementations/WorldRegistrationSystem.sol +++ b/packages/world/src/modules/core/implementations/WorldRegistrationSystem.sol @@ -2,11 +2,11 @@ pragma solidity >=0.8.21; import { Hook, HookLib } from "@latticexyz/store/src/Hook.sol"; -import { ResourceId, ResourceIdInstance } from "@latticexyz/store/src/ResourceId.sol"; +import { ResourceId } from "@latticexyz/store/src/ResourceId.sol"; import { ResourceIds } from "@latticexyz/store/src/codegen/tables/ResourceIds.sol"; import { System } from "../../../System.sol"; -import { WorldContextConsumer, WORLD_CONTEXT_CONSUMER_INTERFACE_ID } from "../../../WorldContext.sol"; +import { WorldContextConsumer, IWorldContextConsumer } from "../../../WorldContext.sol"; import { WorldResourceIdLib, WorldResourceIdInstance } from "../../../WorldResourceId.sol"; import { SystemCall } from "../../../SystemCall.sol"; import { ROOT_NAMESPACE_ID, ROOT_NAME } from "../../../constants.sol"; @@ -18,9 +18,9 @@ import { NamespaceOwner } from "../../../codegen/tables/NamespaceOwner.sol"; import { ResourceAccess } from "../../../codegen/tables/ResourceAccess.sol"; import { UserDelegationControl } from "../../../codegen/tables/UserDelegationControl.sol"; import { NamespaceDelegationControl } from "../../../codegen/tables/NamespaceDelegationControl.sol"; -import { ISystemHook, SYSTEM_HOOK_INTERFACE_ID } from "../../../ISystemHook.sol"; +import { ISystemHook } from "../../../ISystemHook.sol"; import { IWorldErrors } from "../../../IWorldErrors.sol"; -import { DELEGATION_CONTROL_INTERFACE_ID } from "../../../IDelegationControl.sol"; +import { IDelegationControl } from "../../../IDelegationControl.sol"; import { SystemHooks, SystemHooksTableId } from "../../../codegen/tables/SystemHooks.sol"; import { SystemRegistry } from "../../../codegen/tables/SystemRegistry.sol"; @@ -33,7 +33,6 @@ import { FunctionSignatures } from "../../../codegen/tables/FunctionSignatures.s * @dev This contract provides functions related to registering resources other than tables in the World. */ contract WorldRegistrationSystem is System, IWorldErrors { - using ResourceIdInstance for ResourceId; using WorldResourceIdInstance for ResourceId; /** @@ -71,7 +70,7 @@ contract WorldRegistrationSystem is System, IWorldErrors { */ function registerSystemHook(ResourceId systemId, ISystemHook hookAddress, uint8 enabledHooksBitmap) public virtual { // Require the provided address to implement the ISystemHook interface - requireInterface(address(hookAddress), SYSTEM_HOOK_INTERFACE_ID); + requireInterface(address(hookAddress), type(ISystemHook).interfaceId); // Require caller to own the namespace AccessControl.requireOwner(systemId, _msgSender()); @@ -107,7 +106,7 @@ contract WorldRegistrationSystem is System, IWorldErrors { * @param system The system being registered * @param publicAccess Flag indicating if access control check is bypassed */ - function registerSystem(ResourceId systemId, WorldContextConsumer system, bool publicAccess) public virtual { + function registerSystem(ResourceId systemId, System system, bool publicAccess) public virtual { // Require the provided system ID to have type RESOURCE_SYSTEM if (systemId.getType() != RESOURCE_SYSTEM) { revert World_InvalidResourceType(RESOURCE_SYSTEM, systemId, systemId.toString()); @@ -121,7 +120,7 @@ contract WorldRegistrationSystem is System, IWorldErrors { AccessControl.requireOwner(namespaceId, _msgSender()); // Require the provided address to implement the WorldContextConsumer interface - requireInterface(address(system), WORLD_CONTEXT_CONSUMER_INTERFACE_ID); + requireInterface(address(system), type(IWorldContextConsumer).interfaceId); // Require the name to not be the namespace's root name if (systemId.getName() == ROOT_NAME) revert World_InvalidResourceId(systemId, systemId.toString()); @@ -248,7 +247,7 @@ contract WorldRegistrationSystem is System, IWorldErrors { if (Delegation.isLimited(delegationControlId)) { // Require the delegationControl contract to implement the IDelegationControl interface address delegationControl = Systems._getSystem(delegationControlId); - requireInterface(delegationControl, DELEGATION_CONTROL_INTERFACE_ID); + requireInterface(delegationControl, type(IDelegationControl).interfaceId); // Call the delegation control contract's init function SystemCall.callWithHooksOrRevert({ @@ -292,7 +291,7 @@ contract WorldRegistrationSystem is System, IWorldErrors { // Require the delegationControl contract to implement the IDelegationControl interface address delegationControl = Systems._getSystem(delegationControlId); - requireInterface(delegationControl, DELEGATION_CONTROL_INTERFACE_ID); + requireInterface(delegationControl, type(IDelegationControl).interfaceId); // Register the delegation control NamespaceDelegationControl._set(namespaceId, delegationControlId); diff --git a/packages/world/test/SystemHook.t.sol b/packages/world/test/SystemHook.t.sol index 7e1062e0fa..80495ef2c6 100644 --- a/packages/world/test/SystemHook.t.sol +++ b/packages/world/test/SystemHook.t.sol @@ -10,7 +10,7 @@ import { ISystemHook } from "../src/ISystemHook.sol"; contract SystemHookTest is Test, GasReporter { function testFuzzEncode(address hookAddress, bool enableBeforeCallSystem, bool enableAfterCallSystem) public { - uint8 enabledHooksBitmap = 0; + uint8 enabledHooksBitmap; if (enableBeforeCallSystem) enabledHooksBitmap |= BEFORE_CALL_SYSTEM; if (enableAfterCallSystem) enabledHooksBitmap |= AFTER_CALL_SYSTEM; @@ -21,7 +21,7 @@ contract SystemHookTest is Test, GasReporter { } function testFuzzIsEnabled(address hookAddress, bool enableBeforeCallSystem, bool enableAfterCallSystem) public { - uint8 enabledHooksBitmap = 0; + uint8 enabledHooksBitmap; if (enableBeforeCallSystem) enabledHooksBitmap |= BEFORE_CALL_SYSTEM; if (enableAfterCallSystem) enabledHooksBitmap |= AFTER_CALL_SYSTEM; @@ -36,7 +36,7 @@ contract SystemHookTest is Test, GasReporter { bool enableBeforeCallSystem, bool enableAfterCallSystem ) public { - uint8 enabledHooksBitmap = 0; + uint8 enabledHooksBitmap; if (enableBeforeCallSystem) enabledHooksBitmap |= BEFORE_CALL_SYSTEM; if (enableAfterCallSystem) enabledHooksBitmap |= AFTER_CALL_SYSTEM; diff --git a/packages/world/test/World.t.sol b/packages/world/test/World.t.sol index 3e9f6db4d3..1dac97b29d 100644 --- a/packages/world/test/World.t.sol +++ b/packages/world/test/World.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, STORE_HOOK_INTERFACE_ID } from "@latticexyz/store/src/IStoreHook.sol"; +import { IStoreHook } from "@latticexyz/store/src/IStoreHook.sol"; import { StoreCore, StoreCoreInternal } from "@latticexyz/store/src/StoreCore.sol"; import { IStoreErrors } from "@latticexyz/store/src/IStoreErrors.sol"; import { StoreSwitch } from "@latticexyz/store/src/StoreSwitch.sol"; @@ -27,10 +27,10 @@ import { System } from "../src/System.sol"; import { ResourceId, WorldResourceIdLib, WorldResourceIdInstance } from "../src/WorldResourceId.sol"; import { ROOT_NAMESPACE, ROOT_NAME, ROOT_NAMESPACE_ID, UNLIMITED_DELEGATION, WORLD_NAMESPACE_ID, STORE_NAMESPACE_ID } from "../src/constants.sol"; import { RESOURCE_TABLE, RESOURCE_SYSTEM, RESOURCE_NAMESPACE } from "../src/worldResourceTypes.sol"; -import { WorldContextProviderLib, WORLD_CONTEXT_CONSUMER_INTERFACE_ID } from "../src/WorldContext.sol"; +import { WorldContextProviderLib, IWorldContextConsumer } from "../src/WorldContext.sol"; import { SystemHook } from "../src/SystemHook.sol"; import { BEFORE_CALL_SYSTEM, AFTER_CALL_SYSTEM } from "../src/systemHookTypes.sol"; -import { Module, MODULE_INTERFACE_ID } from "../src/Module.sol"; +import { Module, IModule } from "../src/Module.sol"; import { NamespaceOwner, NamespaceOwnerTableId } from "../src/codegen/tables/NamespaceOwner.sol"; import { ResourceAccess } from "../src/codegen/tables/ResourceAccess.sol"; @@ -48,7 +48,7 @@ import { FunctionSelectors } from "../src/codegen/tables/FunctionSelectors.sol"; import { IBaseWorld } from "../src/codegen/interfaces/IBaseWorld.sol"; import { IWorldErrors } from "../src/IWorldErrors.sol"; -import { ISystemHook, SYSTEM_HOOK_INTERFACE_ID } from "../src/ISystemHook.sol"; +import { ISystemHook } from "../src/ISystemHook.sol"; import { Bool } from "./codegen/tables/Bool.sol"; import { TwoFields, TwoFieldsData } from "./codegen/tables/TwoFields.sol"; @@ -281,7 +281,7 @@ contract WorldTest is Test, GasReporter { abi.encodeWithSelector( IWorldErrors.World_InterfaceNotSupported.selector, Module(address(world)), // The World contract does not implement the IModule interface - MODULE_INTERFACE_ID + type(IModule).interfaceId ) ); world.installModule(Module(address(world)), new bytes(0)); @@ -291,7 +291,7 @@ contract WorldTest is Test, GasReporter { abi.encodeWithSelector( IWorldErrors.World_InterfaceNotSupported.selector, Module(address(world)), // The World contract does not implement the IModule interface - MODULE_INTERFACE_ID + type(IModule).interfaceId ) ); world.installRootModule(Module(address(world)), new bytes(0)); @@ -587,7 +587,7 @@ contract WorldTest is Test, GasReporter { abi.encodeWithSelector( IWorldErrors.World_InterfaceNotSupported.selector, address(world), - WORLD_CONTEXT_CONSUMER_INTERFACE_ID + type(IWorldContextConsumer).interfaceId ) ); world.registerSystem( @@ -1209,7 +1209,11 @@ contract WorldTest is Test, GasReporter { // Expect an error when trying to register an address that doesn't implement the IStoreHook interface vm.expectRevert( - abi.encodeWithSelector(IWorldErrors.World_InterfaceNotSupported.selector, address(world), STORE_HOOK_INTERFACE_ID) + abi.encodeWithSelector( + IWorldErrors.World_InterfaceNotSupported.selector, + address(world), + type(IStoreHook).interfaceId + ) ); world.registerStoreHook( tableId, @@ -1306,7 +1310,7 @@ contract WorldTest is Test, GasReporter { abi.encodeWithSelector( IWorldErrors.World_InterfaceNotSupported.selector, address(world), - SYSTEM_HOOK_INTERFACE_ID + type(ISystemHook).interfaceId ) ); world.registerSystemHook( diff --git a/packages/world/test/WorldResourceId.t.sol b/packages/world/test/WorldResourceId.t.sol index e8e4d8dcea..e4dfdf3e9e 100644 --- a/packages/world/test/WorldResourceId.t.sol +++ b/packages/world/test/WorldResourceId.t.sol @@ -3,13 +3,12 @@ pragma solidity >=0.8.21; import { Test, console } from "forge-std/Test.sol"; import { GasReporter } from "@latticexyz/gas-report/src/GasReporter.sol"; -import { ResourceId, ResourceIdLib, ResourceIdInstance } from "@latticexyz/store/src/ResourceId.sol"; +import { ResourceId, ResourceIdLib } from "@latticexyz/store/src/ResourceId.sol"; import { WorldResourceIdLib, WorldResourceIdInstance, NAMESPACE_BITS, NAME_BITS, TYPE_BITS } from "../src/WorldResourceId.sol"; import { RESOURCE_SYSTEM } from "../src/worldResourceTypes.sol"; contract WorldResourceIdTest is Test, GasReporter { - using ResourceIdInstance for ResourceId; using WorldResourceIdInstance for ResourceId; function testEncode() public {