diff --git a/.changeset/strong-months-push.md b/.changeset/strong-months-push.md new file mode 100644 index 0000000000..42b321ce1c --- /dev/null +++ b/.changeset/strong-months-push.md @@ -0,0 +1,9 @@ +--- +"@latticexyz/store": major +--- + +- `StoreCore`'s `initialize` function is split into `initialize` (to set the `StoreSwitch`'s `storeAddress`) and `registerCoreTables` (to register the `Tables` and `StoreHooks` tables). + The purpose of this is to give consumers more granular control over the setup flow. + +- The `StoreRead` contract no longer calls `StoreCore.initialize` in its constructor. + `StoreCore` consumers are expected to call `StoreCore.initialize` and `StoreCore.registerCoreTable` in their own setup logic. diff --git a/.changeset/tricky-beds-kiss.md b/.changeset/tricky-beds-kiss.md new file mode 100644 index 0000000000..151ec9202a --- /dev/null +++ b/.changeset/tricky-beds-kiss.md @@ -0,0 +1,24 @@ +--- +"@latticexyz/cli": patch +"@latticexyz/world": minor +--- + +- The `World` contract now has an `initialize` function, which can be called once by the creator of the World to install the core module. + This change allows the registration of all core tables to happen in the `CoreModule`, so no table metadata has to be included in the `World`'s bytecode. + + ```solidity + interface IBaseWorld { + function initialize(IModule coreModule) public; + } + ``` + +- The `World` contract now stores the original creator of the `World` in an immutable state variable. + It is used internally to only allow the original creator to initialize the `World` in a separate transaction. + + ```solidity + interface IBaseWorld { + function creator() external view returns (address); + } + ``` + +- The deploy script is updated to use the `World`'s `initialize` function to install the `CoreModule` instead of `registerRootModule` as before. diff --git a/packages/cli/contracts/test/Tablegen.t.sol b/packages/cli/contracts/test/Tablegen.t.sol index cf840e6387..29f6ec8b29 100644 --- a/packages/cli/contracts/test/Tablegen.t.sol +++ b/packages/cli/contracts/test/Tablegen.t.sol @@ -2,13 +2,13 @@ pragma solidity >=0.8.0; import "forge-std/Test.sol"; -import { StoreReadWithStubs } from "@latticexyz/store/src/StoreReadWithStubs.sol"; +import { StoreMock } from "@latticexyz/store/test/StoreMock.sol"; import { Statics, StaticsData, Dynamics1, Dynamics1Data, Dynamics2, Dynamics2Data, Singleton, Ephemeral } from "../src/codegen/Tables.sol"; import { Enum1, Enum2 } from "../src/codegen/Types.sol"; -contract TablegenTest is Test, StoreReadWithStubs { +contract TablegenTest is Test, StoreMock { function testStaticsSetAndGet() public { Statics.register(); diff --git a/packages/cli/src/utils/deploy.ts b/packages/cli/src/utils/deploy.ts index 76b2df2993..a9d1cb31fc 100644 --- a/packages/cli/src/utils/deploy.ts +++ b/packages/cli/src/utils/deploy.ts @@ -136,7 +136,7 @@ export async function deploy( // Install core Modules if (!worldAddress) { console.log(chalk.blue("Installing core World modules")); - await fastTxExecute(WorldContract, "installRootModule", [await modulePromises.CoreModule, "0x"], confirmations); + await fastTxExecute(WorldContract, "initialize", [await modulePromises.CoreModule], confirmations); console.log(chalk.green("Installed core World modules")); } diff --git a/packages/store/abi/StoreMock.sol/StoreMock.abi.json b/packages/store/abi/StoreMock.sol/StoreMock.abi.json index 2bdd2738ee..06aaab4969 100644 --- a/packages/store/abi/StoreMock.sol/StoreMock.abi.json +++ b/packages/store/abi/StoreMock.sol/StoreMock.abi.json @@ -1,4 +1,9 @@ [ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, { "inputs": [ { diff --git a/packages/store/abi/StoreMock.sol/StoreMock.abi.json.d.ts b/packages/store/abi/StoreMock.sol/StoreMock.abi.json.d.ts index e9dfdc5fd2..7c1b063fd5 100644 --- a/packages/store/abi/StoreMock.sol/StoreMock.abi.json.d.ts +++ b/packages/store/abi/StoreMock.sol/StoreMock.abi.json.d.ts @@ -1,4 +1,9 @@ declare const abi: [ + { + inputs: []; + stateMutability: "nonpayable"; + type: "constructor"; + }, { inputs: [ { diff --git a/packages/store/abi/StoreRead.sol/StoreRead.abi.json b/packages/store/abi/StoreRead.sol/StoreRead.abi.json index 1f8dd917db..d667734d9d 100644 --- a/packages/store/abi/StoreRead.sol/StoreRead.abi.json +++ b/packages/store/abi/StoreRead.sol/StoreRead.abi.json @@ -1,9 +1,4 @@ [ - { - "inputs": [], - "stateMutability": "nonpayable", - "type": "constructor" - }, { "inputs": [ { @@ -25,128 +20,11 @@ "name": "FieldLayoutLib_StaticLengthIsZero", "type": "error" }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "length", - "type": "uint256" - } - ], - "name": "SchemaLib_InvalidLength", - "type": "error" - }, - { - "inputs": [], - "name": "SchemaLib_StaticTypeAfterDynamicType", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - }, - { - "internalType": "uint256", - "name": "start", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "end", - "type": "uint256" - } - ], - "name": "Slice_OutOfBounds", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "expected", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "received", - "type": "uint256" - } - ], - "name": "StoreCore_InvalidDataLength", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "expected", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "received", - "type": "uint256" - } - ], - "name": "StoreCore_InvalidFieldNamesLength", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "expected", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "received", - "type": "uint256" - } - ], - "name": "StoreCore_InvalidKeyNamesLength", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "expected", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "received", - "type": "uint256" - } - ], - "name": "StoreCore_InvalidValueSchemaLength", - "type": "error" - }, { "inputs": [], "name": "StoreCore_NotDynamicField", "type": "error" }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "tableId", - "type": "bytes32" - }, - { - "internalType": "string", - "name": "tableIdString", - "type": "string" - } - ], - "name": "StoreCore_TableAlreadyExists", - "type": "error" - }, { "inputs": [ { diff --git a/packages/store/abi/StoreRead.sol/StoreRead.abi.json.d.ts b/packages/store/abi/StoreRead.sol/StoreRead.abi.json.d.ts index 677134cc99..b55d3b54f0 100644 --- a/packages/store/abi/StoreRead.sol/StoreRead.abi.json.d.ts +++ b/packages/store/abi/StoreRead.sol/StoreRead.abi.json.d.ts @@ -1,9 +1,4 @@ declare const abi: [ - { - inputs: []; - stateMutability: "nonpayable"; - type: "constructor"; - }, { inputs: [ { @@ -25,128 +20,11 @@ declare const abi: [ name: "FieldLayoutLib_StaticLengthIsZero"; type: "error"; }, - { - inputs: [ - { - internalType: "uint256"; - name: "length"; - type: "uint256"; - } - ]; - name: "SchemaLib_InvalidLength"; - type: "error"; - }, - { - inputs: []; - name: "SchemaLib_StaticTypeAfterDynamicType"; - type: "error"; - }, - { - inputs: [ - { - internalType: "bytes"; - name: "data"; - type: "bytes"; - }, - { - internalType: "uint256"; - name: "start"; - type: "uint256"; - }, - { - internalType: "uint256"; - name: "end"; - type: "uint256"; - } - ]; - name: "Slice_OutOfBounds"; - type: "error"; - }, - { - inputs: [ - { - internalType: "uint256"; - name: "expected"; - type: "uint256"; - }, - { - internalType: "uint256"; - name: "received"; - type: "uint256"; - } - ]; - name: "StoreCore_InvalidDataLength"; - type: "error"; - }, - { - inputs: [ - { - internalType: "uint256"; - name: "expected"; - type: "uint256"; - }, - { - internalType: "uint256"; - name: "received"; - type: "uint256"; - } - ]; - name: "StoreCore_InvalidFieldNamesLength"; - type: "error"; - }, - { - inputs: [ - { - internalType: "uint256"; - name: "expected"; - type: "uint256"; - }, - { - internalType: "uint256"; - name: "received"; - type: "uint256"; - } - ]; - name: "StoreCore_InvalidKeyNamesLength"; - type: "error"; - }, - { - inputs: [ - { - internalType: "uint256"; - name: "expected"; - type: "uint256"; - }, - { - internalType: "uint256"; - name: "received"; - type: "uint256"; - } - ]; - name: "StoreCore_InvalidValueSchemaLength"; - type: "error"; - }, { inputs: []; name: "StoreCore_NotDynamicField"; type: "error"; }, - { - inputs: [ - { - internalType: "bytes32"; - name: "tableId"; - type: "bytes32"; - }, - { - internalType: "string"; - name: "tableIdString"; - type: "string"; - } - ]; - name: "StoreCore_TableAlreadyExists"; - type: "error"; - }, { inputs: [ { diff --git a/packages/store/abi/StoreReadWithStubs.sol/StoreReadWithStubs.abi.json b/packages/store/abi/StoreReadWithStubs.sol/StoreReadWithStubs.abi.json deleted file mode 100644 index ff1fab95f9..0000000000 --- a/packages/store/abi/StoreReadWithStubs.sol/StoreReadWithStubs.abi.json +++ /dev/null @@ -1,780 +0,0 @@ -[ - { - "inputs": [ - { - "internalType": "uint256", - "name": "length", - "type": "uint256" - } - ], - "name": "FieldLayoutLib_InvalidLength", - "type": "error" - }, - { - "inputs": [], - "name": "FieldLayoutLib_StaticLengthDoesNotFitInAWord", - "type": "error" - }, - { - "inputs": [], - "name": "FieldLayoutLib_StaticLengthIsZero", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "length", - "type": "uint256" - } - ], - "name": "SchemaLib_InvalidLength", - "type": "error" - }, - { - "inputs": [], - "name": "SchemaLib_StaticTypeAfterDynamicType", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - }, - { - "internalType": "uint256", - "name": "start", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "end", - "type": "uint256" - } - ], - "name": "Slice_OutOfBounds", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "length", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "received", - "type": "uint256" - } - ], - "name": "StoreCore_DataIndexOverflow", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "expected", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "received", - "type": "uint256" - } - ], - "name": "StoreCore_InvalidDataLength", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "expected", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "received", - "type": "uint256" - } - ], - "name": "StoreCore_InvalidFieldNamesLength", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "expected", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "received", - "type": "uint256" - } - ], - "name": "StoreCore_InvalidKeyNamesLength", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "expected", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "received", - "type": "uint256" - } - ], - "name": "StoreCore_InvalidValueSchemaLength", - "type": "error" - }, - { - "inputs": [], - "name": "StoreCore_NotDynamicField", - "type": "error" - }, - { - "inputs": [], - "name": "StoreCore_NotImplemented", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "tableId", - "type": "bytes32" - }, - { - "internalType": "string", - "name": "tableIdString", - "type": "string" - } - ], - "name": "StoreCore_TableAlreadyExists", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "tableId", - "type": "bytes32" - }, - { - "internalType": "string", - "name": "tableIdString", - "type": "string" - } - ], - "name": "StoreCore_TableNotFound", - "type": "error" - }, - { - "inputs": [], - "name": "StoreReadWithStubs_NotImplemented", - "type": "error" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "bytes32", - "name": "table", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "bytes32[]", - "name": "key", - "type": "bytes32[]" - } - ], - "name": "StoreDeleteRecord", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "bytes32", - "name": "table", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "bytes32[]", - "name": "key", - "type": "bytes32[]" - }, - { - "indexed": false, - "internalType": "bytes", - "name": "data", - "type": "bytes" - } - ], - "name": "StoreEphemeralRecord", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "bytes32", - "name": "table", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "bytes32[]", - "name": "key", - "type": "bytes32[]" - }, - { - "indexed": false, - "internalType": "uint8", - "name": "schemaIndex", - "type": "uint8" - }, - { - "indexed": false, - "internalType": "bytes", - "name": "data", - "type": "bytes" - } - ], - "name": "StoreSetField", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "bytes32", - "name": "table", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "bytes32[]", - "name": "key", - "type": "bytes32[]" - }, - { - "indexed": false, - "internalType": "bytes", - "name": "data", - "type": "bytes" - } - ], - "name": "StoreSetRecord", - "type": "event" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - }, - { - "internalType": "bytes32[]", - "name": "", - "type": "bytes32[]" - }, - { - "internalType": "FieldLayout", - "name": "", - "type": "bytes32" - } - ], - "name": "deleteRecord", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - }, - { - "internalType": "bytes32[]", - "name": "", - "type": "bytes32[]" - }, - { - "internalType": "bytes", - "name": "", - "type": "bytes" - }, - { - "internalType": "FieldLayout", - "name": "", - "type": "bytes32" - } - ], - "name": "emitEphemeralRecord", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "table", - "type": "bytes32" - }, - { - "internalType": "bytes32[]", - "name": "key", - "type": "bytes32[]" - }, - { - "internalType": "uint8", - "name": "schemaIndex", - "type": "uint8" - }, - { - "internalType": "FieldLayout", - "name": "fieldLayout", - "type": "bytes32" - } - ], - "name": "getField", - "outputs": [ - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "table", - "type": "bytes32" - } - ], - "name": "getFieldLayout", - "outputs": [ - { - "internalType": "FieldLayout", - "name": "fieldLayout", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "tableId", - "type": "bytes32" - }, - { - "internalType": "bytes32[]", - "name": "key", - "type": "bytes32[]" - }, - { - "internalType": "uint8", - "name": "schemaIndex", - "type": "uint8" - }, - { - "internalType": "FieldLayout", - "name": "fieldLayout", - "type": "bytes32" - } - ], - "name": "getFieldLength", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "tableId", - "type": "bytes32" - }, - { - "internalType": "bytes32[]", - "name": "key", - "type": "bytes32[]" - }, - { - "internalType": "uint8", - "name": "schemaIndex", - "type": "uint8" - }, - { - "internalType": "FieldLayout", - "name": "fieldLayout", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "start", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "end", - "type": "uint256" - } - ], - "name": "getFieldSlice", - "outputs": [ - { - "internalType": "bytes", - "name": "", - "type": "bytes" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "table", - "type": "bytes32" - } - ], - "name": "getKeySchema", - "outputs": [ - { - "internalType": "Schema", - "name": "schema", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "table", - "type": "bytes32" - }, - { - "internalType": "bytes32[]", - "name": "key", - "type": "bytes32[]" - }, - { - "internalType": "FieldLayout", - "name": "fieldLayout", - "type": "bytes32" - } - ], - "name": "getRecord", - "outputs": [ - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "table", - "type": "bytes32" - } - ], - "name": "getValueSchema", - "outputs": [ - { - "internalType": "Schema", - "name": "schema", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - }, - { - "internalType": "bytes32[]", - "name": "", - "type": "bytes32[]" - }, - { - "internalType": "uint8", - "name": "", - "type": "uint8" - }, - { - "internalType": "uint256", - "name": "", - "type": "uint256" - }, - { - "internalType": "FieldLayout", - "name": "", - "type": "bytes32" - } - ], - "name": "popFromField", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - }, - { - "internalType": "bytes32[]", - "name": "", - "type": "bytes32[]" - }, - { - "internalType": "uint8", - "name": "", - "type": "uint8" - }, - { - "internalType": "bytes", - "name": "", - "type": "bytes" - }, - { - "internalType": "FieldLayout", - "name": "", - "type": "bytes32" - } - ], - "name": "pushToField", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - }, - { - "internalType": "contract IStoreHook", - "name": "", - "type": "address" - }, - { - "internalType": "uint8", - "name": "", - "type": "uint8" - } - ], - "name": "registerStoreHook", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - }, - { - "internalType": "FieldLayout", - "name": "", - "type": "bytes32" - }, - { - "internalType": "Schema", - "name": "", - "type": "bytes32" - }, - { - "internalType": "Schema", - "name": "", - "type": "bytes32" - }, - { - "internalType": "string[]", - "name": "", - "type": "string[]" - }, - { - "internalType": "string[]", - "name": "", - "type": "string[]" - } - ], - "name": "registerTable", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - }, - { - "internalType": "bytes32[]", - "name": "", - "type": "bytes32[]" - }, - { - "internalType": "uint8", - "name": "", - "type": "uint8" - }, - { - "internalType": "bytes", - "name": "", - "type": "bytes" - }, - { - "internalType": "FieldLayout", - "name": "", - "type": "bytes32" - } - ], - "name": "setField", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - }, - { - "internalType": "bytes32[]", - "name": "", - "type": "bytes32[]" - }, - { - "internalType": "bytes", - "name": "", - "type": "bytes" - }, - { - "internalType": "FieldLayout", - "name": "", - "type": "bytes32" - } - ], - "name": "setRecord", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - }, - { - "internalType": "contract IStoreHook", - "name": "", - "type": "address" - } - ], - "name": "unregisterStoreHook", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - }, - { - "internalType": "bytes32[]", - "name": "", - "type": "bytes32[]" - }, - { - "internalType": "uint8", - "name": "", - "type": "uint8" - }, - { - "internalType": "uint256", - "name": "", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "", - "type": "bytes" - }, - { - "internalType": "FieldLayout", - "name": "", - "type": "bytes32" - } - ], - "name": "updateInField", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - } -] \ No newline at end of file diff --git a/packages/store/abi/StoreReadWithStubs.sol/StoreReadWithStubs.abi.json.d.ts b/packages/store/abi/StoreReadWithStubs.sol/StoreReadWithStubs.abi.json.d.ts deleted file mode 100644 index 667d11271a..0000000000 --- a/packages/store/abi/StoreReadWithStubs.sol/StoreReadWithStubs.abi.json.d.ts +++ /dev/null @@ -1,781 +0,0 @@ -declare const abi: [ - { - inputs: [ - { - internalType: "uint256"; - name: "length"; - type: "uint256"; - } - ]; - name: "FieldLayoutLib_InvalidLength"; - type: "error"; - }, - { - inputs: []; - name: "FieldLayoutLib_StaticLengthDoesNotFitInAWord"; - type: "error"; - }, - { - inputs: []; - name: "FieldLayoutLib_StaticLengthIsZero"; - type: "error"; - }, - { - inputs: [ - { - internalType: "uint256"; - name: "length"; - type: "uint256"; - } - ]; - name: "SchemaLib_InvalidLength"; - type: "error"; - }, - { - inputs: []; - name: "SchemaLib_StaticTypeAfterDynamicType"; - type: "error"; - }, - { - inputs: [ - { - internalType: "bytes"; - name: "data"; - type: "bytes"; - }, - { - internalType: "uint256"; - name: "start"; - type: "uint256"; - }, - { - internalType: "uint256"; - name: "end"; - type: "uint256"; - } - ]; - name: "Slice_OutOfBounds"; - type: "error"; - }, - { - inputs: [ - { - internalType: "uint256"; - name: "length"; - type: "uint256"; - }, - { - internalType: "uint256"; - name: "received"; - type: "uint256"; - } - ]; - name: "StoreCore_DataIndexOverflow"; - type: "error"; - }, - { - inputs: [ - { - internalType: "uint256"; - name: "expected"; - type: "uint256"; - }, - { - internalType: "uint256"; - name: "received"; - type: "uint256"; - } - ]; - name: "StoreCore_InvalidDataLength"; - type: "error"; - }, - { - inputs: [ - { - internalType: "uint256"; - name: "expected"; - type: "uint256"; - }, - { - internalType: "uint256"; - name: "received"; - type: "uint256"; - } - ]; - name: "StoreCore_InvalidFieldNamesLength"; - type: "error"; - }, - { - inputs: [ - { - internalType: "uint256"; - name: "expected"; - type: "uint256"; - }, - { - internalType: "uint256"; - name: "received"; - type: "uint256"; - } - ]; - name: "StoreCore_InvalidKeyNamesLength"; - type: "error"; - }, - { - inputs: [ - { - internalType: "uint256"; - name: "expected"; - type: "uint256"; - }, - { - internalType: "uint256"; - name: "received"; - type: "uint256"; - } - ]; - name: "StoreCore_InvalidValueSchemaLength"; - type: "error"; - }, - { - inputs: []; - name: "StoreCore_NotDynamicField"; - type: "error"; - }, - { - inputs: []; - name: "StoreCore_NotImplemented"; - type: "error"; - }, - { - inputs: [ - { - internalType: "bytes32"; - name: "tableId"; - type: "bytes32"; - }, - { - internalType: "string"; - name: "tableIdString"; - type: "string"; - } - ]; - name: "StoreCore_TableAlreadyExists"; - type: "error"; - }, - { - inputs: [ - { - internalType: "bytes32"; - name: "tableId"; - type: "bytes32"; - }, - { - internalType: "string"; - name: "tableIdString"; - type: "string"; - } - ]; - name: "StoreCore_TableNotFound"; - type: "error"; - }, - { - inputs: []; - name: "StoreReadWithStubs_NotImplemented"; - type: "error"; - }, - { - anonymous: false; - inputs: [ - { - indexed: false; - internalType: "bytes32"; - name: "table"; - type: "bytes32"; - }, - { - indexed: false; - internalType: "bytes32[]"; - name: "key"; - type: "bytes32[]"; - } - ]; - name: "StoreDeleteRecord"; - type: "event"; - }, - { - anonymous: false; - inputs: [ - { - indexed: false; - internalType: "bytes32"; - name: "table"; - type: "bytes32"; - }, - { - indexed: false; - internalType: "bytes32[]"; - name: "key"; - type: "bytes32[]"; - }, - { - indexed: false; - internalType: "bytes"; - name: "data"; - type: "bytes"; - } - ]; - name: "StoreEphemeralRecord"; - type: "event"; - }, - { - anonymous: false; - inputs: [ - { - indexed: false; - internalType: "bytes32"; - name: "table"; - type: "bytes32"; - }, - { - indexed: false; - internalType: "bytes32[]"; - name: "key"; - type: "bytes32[]"; - }, - { - indexed: false; - internalType: "uint8"; - name: "schemaIndex"; - type: "uint8"; - }, - { - indexed: false; - internalType: "bytes"; - name: "data"; - type: "bytes"; - } - ]; - name: "StoreSetField"; - type: "event"; - }, - { - anonymous: false; - inputs: [ - { - indexed: false; - internalType: "bytes32"; - name: "table"; - type: "bytes32"; - }, - { - indexed: false; - internalType: "bytes32[]"; - name: "key"; - type: "bytes32[]"; - }, - { - indexed: false; - internalType: "bytes"; - name: "data"; - type: "bytes"; - } - ]; - name: "StoreSetRecord"; - type: "event"; - }, - { - inputs: [ - { - internalType: "bytes32"; - name: ""; - type: "bytes32"; - }, - { - internalType: "bytes32[]"; - name: ""; - type: "bytes32[]"; - }, - { - internalType: "FieldLayout"; - name: ""; - type: "bytes32"; - } - ]; - name: "deleteRecord"; - outputs: []; - stateMutability: "nonpayable"; - type: "function"; - }, - { - inputs: [ - { - internalType: "bytes32"; - name: ""; - type: "bytes32"; - }, - { - internalType: "bytes32[]"; - name: ""; - type: "bytes32[]"; - }, - { - internalType: "bytes"; - name: ""; - type: "bytes"; - }, - { - internalType: "FieldLayout"; - name: ""; - type: "bytes32"; - } - ]; - name: "emitEphemeralRecord"; - outputs: []; - stateMutability: "nonpayable"; - type: "function"; - }, - { - inputs: [ - { - internalType: "bytes32"; - name: "table"; - type: "bytes32"; - }, - { - internalType: "bytes32[]"; - name: "key"; - type: "bytes32[]"; - }, - { - internalType: "uint8"; - name: "schemaIndex"; - type: "uint8"; - }, - { - internalType: "FieldLayout"; - name: "fieldLayout"; - type: "bytes32"; - } - ]; - name: "getField"; - outputs: [ - { - internalType: "bytes"; - name: "data"; - type: "bytes"; - } - ]; - stateMutability: "view"; - type: "function"; - }, - { - inputs: [ - { - internalType: "bytes32"; - name: "table"; - type: "bytes32"; - } - ]; - name: "getFieldLayout"; - outputs: [ - { - internalType: "FieldLayout"; - name: "fieldLayout"; - type: "bytes32"; - } - ]; - stateMutability: "view"; - type: "function"; - }, - { - inputs: [ - { - internalType: "bytes32"; - name: "tableId"; - type: "bytes32"; - }, - { - internalType: "bytes32[]"; - name: "key"; - type: "bytes32[]"; - }, - { - internalType: "uint8"; - name: "schemaIndex"; - type: "uint8"; - }, - { - internalType: "FieldLayout"; - name: "fieldLayout"; - type: "bytes32"; - } - ]; - name: "getFieldLength"; - outputs: [ - { - internalType: "uint256"; - name: ""; - type: "uint256"; - } - ]; - stateMutability: "view"; - type: "function"; - }, - { - inputs: [ - { - internalType: "bytes32"; - name: "tableId"; - type: "bytes32"; - }, - { - internalType: "bytes32[]"; - name: "key"; - type: "bytes32[]"; - }, - { - internalType: "uint8"; - name: "schemaIndex"; - type: "uint8"; - }, - { - internalType: "FieldLayout"; - name: "fieldLayout"; - type: "bytes32"; - }, - { - internalType: "uint256"; - name: "start"; - type: "uint256"; - }, - { - internalType: "uint256"; - name: "end"; - type: "uint256"; - } - ]; - name: "getFieldSlice"; - outputs: [ - { - internalType: "bytes"; - name: ""; - type: "bytes"; - } - ]; - stateMutability: "view"; - type: "function"; - }, - { - inputs: [ - { - internalType: "bytes32"; - name: "table"; - type: "bytes32"; - } - ]; - name: "getKeySchema"; - outputs: [ - { - internalType: "Schema"; - name: "schema"; - type: "bytes32"; - } - ]; - stateMutability: "view"; - type: "function"; - }, - { - inputs: [ - { - internalType: "bytes32"; - name: "table"; - type: "bytes32"; - }, - { - internalType: "bytes32[]"; - name: "key"; - type: "bytes32[]"; - }, - { - internalType: "FieldLayout"; - name: "fieldLayout"; - type: "bytes32"; - } - ]; - name: "getRecord"; - outputs: [ - { - internalType: "bytes"; - name: "data"; - type: "bytes"; - } - ]; - stateMutability: "view"; - type: "function"; - }, - { - inputs: [ - { - internalType: "bytes32"; - name: "table"; - type: "bytes32"; - } - ]; - name: "getValueSchema"; - outputs: [ - { - internalType: "Schema"; - name: "schema"; - type: "bytes32"; - } - ]; - stateMutability: "view"; - type: "function"; - }, - { - inputs: [ - { - internalType: "bytes32"; - name: ""; - type: "bytes32"; - }, - { - internalType: "bytes32[]"; - name: ""; - type: "bytes32[]"; - }, - { - internalType: "uint8"; - name: ""; - type: "uint8"; - }, - { - internalType: "uint256"; - name: ""; - type: "uint256"; - }, - { - internalType: "FieldLayout"; - name: ""; - type: "bytes32"; - } - ]; - name: "popFromField"; - outputs: []; - stateMutability: "nonpayable"; - type: "function"; - }, - { - inputs: [ - { - internalType: "bytes32"; - name: ""; - type: "bytes32"; - }, - { - internalType: "bytes32[]"; - name: ""; - type: "bytes32[]"; - }, - { - internalType: "uint8"; - name: ""; - type: "uint8"; - }, - { - internalType: "bytes"; - name: ""; - type: "bytes"; - }, - { - internalType: "FieldLayout"; - name: ""; - type: "bytes32"; - } - ]; - name: "pushToField"; - outputs: []; - stateMutability: "nonpayable"; - type: "function"; - }, - { - inputs: [ - { - internalType: "bytes32"; - name: ""; - type: "bytes32"; - }, - { - internalType: "contract IStoreHook"; - name: ""; - type: "address"; - }, - { - internalType: "uint8"; - name: ""; - type: "uint8"; - } - ]; - name: "registerStoreHook"; - outputs: []; - stateMutability: "nonpayable"; - type: "function"; - }, - { - inputs: [ - { - internalType: "bytes32"; - name: ""; - type: "bytes32"; - }, - { - internalType: "FieldLayout"; - name: ""; - type: "bytes32"; - }, - { - internalType: "Schema"; - name: ""; - type: "bytes32"; - }, - { - internalType: "Schema"; - name: ""; - type: "bytes32"; - }, - { - internalType: "string[]"; - name: ""; - type: "string[]"; - }, - { - internalType: "string[]"; - name: ""; - type: "string[]"; - } - ]; - name: "registerTable"; - outputs: []; - stateMutability: "nonpayable"; - type: "function"; - }, - { - inputs: [ - { - internalType: "bytes32"; - name: ""; - type: "bytes32"; - }, - { - internalType: "bytes32[]"; - name: ""; - type: "bytes32[]"; - }, - { - internalType: "uint8"; - name: ""; - type: "uint8"; - }, - { - internalType: "bytes"; - name: ""; - type: "bytes"; - }, - { - internalType: "FieldLayout"; - name: ""; - type: "bytes32"; - } - ]; - name: "setField"; - outputs: []; - stateMutability: "nonpayable"; - type: "function"; - }, - { - inputs: [ - { - internalType: "bytes32"; - name: ""; - type: "bytes32"; - }, - { - internalType: "bytes32[]"; - name: ""; - type: "bytes32[]"; - }, - { - internalType: "bytes"; - name: ""; - type: "bytes"; - }, - { - internalType: "FieldLayout"; - name: ""; - type: "bytes32"; - } - ]; - name: "setRecord"; - outputs: []; - stateMutability: "nonpayable"; - type: "function"; - }, - { - inputs: [ - { - internalType: "bytes32"; - name: ""; - type: "bytes32"; - }, - { - internalType: "contract IStoreHook"; - name: ""; - type: "address"; - } - ]; - name: "unregisterStoreHook"; - outputs: []; - stateMutability: "nonpayable"; - type: "function"; - }, - { - inputs: [ - { - internalType: "bytes32"; - name: ""; - type: "bytes32"; - }, - { - internalType: "bytes32[]"; - name: ""; - type: "bytes32[]"; - }, - { - internalType: "uint8"; - name: ""; - type: "uint8"; - }, - { - internalType: "uint256"; - name: ""; - type: "uint256"; - }, - { - internalType: "bytes"; - name: ""; - type: "bytes"; - }, - { - internalType: "FieldLayout"; - name: ""; - type: "bytes32"; - } - ]; - name: "updateInField"; - outputs: []; - stateMutability: "nonpayable"; - type: "function"; - } -]; -export default abi; diff --git a/packages/store/gas-report.json b/packages/store/gas-report.json index cf9bba9495..0899ffa898 100644 --- a/packages/store/gas-report.json +++ b/packages/store/gas-report.json @@ -309,7 +309,7 @@ "file": "test/KeyEncoding.t.sol", "test": "testRegisterAndGetFieldLayout", "name": "register KeyEncoding table", - "gasUsed": 697846 + "gasUsed": 697905 }, { "file": "test/Mixed.t.sol", @@ -903,7 +903,7 @@ "file": "test/tables/Callbacks.t.sol", "test": "testSetAndGet", "name": "Callbacks: push 1 element", - "gasUsed": 39000 + "gasUsed": 39001 }, { "file": "test/tables/StoreHooks.t.sol", @@ -1059,7 +1059,7 @@ "file": "test/Vector2.t.sol", "test": "testRegisterAndGetFieldLayout", "name": "register Vector2 field layout", - "gasUsed": 421468 + "gasUsed": 421491 }, { "file": "test/Vector2.t.sol", diff --git a/packages/store/src/StoreCore.sol b/packages/store/src/StoreCore.sol index 7c52c5280d..a9f2540143 100644 --- a/packages/store/src/StoreCore.sol +++ b/packages/store/src/StoreCore.sol @@ -23,7 +23,7 @@ library StoreCore { event StoreEphemeralRecord(bytes32 table, bytes32[] key, bytes data); /** - * Initialize internal tables. + * Intialize the store address to use in StoreSwitch. * Consumers must call this function in their constructor. */ function initialize() internal { @@ -31,8 +31,15 @@ library StoreCore { // If StoreSwitch is called in the context of a Store contract (storeAddress == address(this)), // StoreSwitch uses internal methods to write data instead of external calls. StoreSwitch.setStoreAddress(address(this)); + } - // Register internal tables + /** + * Register core tables. + * Consumers must call this function in their constructor before setting + * any table data to allow indexers to decode table events. + */ + function registerCoreTables() internal { + // Register core tables Tables.register(); StoreHooks.register(); } diff --git a/packages/store/src/StoreRead.sol b/packages/store/src/StoreRead.sol index 83ca38b864..28fc1ff91a 100644 --- a/packages/store/src/StoreRead.sol +++ b/packages/store/src/StoreRead.sol @@ -7,10 +7,6 @@ import { FieldLayout } from "./FieldLayout.sol"; import { Schema } from "./Schema.sol"; contract StoreRead is IStoreRead { - constructor() { - StoreCore.initialize(); - } - function getFieldLayout(bytes32 table) public view virtual returns (FieldLayout fieldLayout) { fieldLayout = StoreCore.getFieldLayout(table); } diff --git a/packages/store/src/StoreReadWithStubs.sol b/packages/store/src/StoreReadWithStubs.sol deleted file mode 100644 index 71c55ccfca..0000000000 --- a/packages/store/src/StoreReadWithStubs.sol +++ /dev/null @@ -1,87 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.8.0; - -import { IStore, IStoreHook } from "./IStore.sol"; -import { StoreCore } from "./StoreCore.sol"; -import { FieldLayout } from "./FieldLayout.sol"; -import { Schema } from "./Schema.sol"; -import { StoreRead } from "./StoreRead.sol"; - -/** - * StoreReadWithStubs is not abstract and has signatures for all IStore methods, - * but only implements read methods (inheriting from StoreRead), - * so it can be used as IStore mock for testing or when write methods are not needed. - */ -contract StoreReadWithStubs is IStore, StoreRead { - error StoreReadWithStubs_NotImplemented(); - - /** - * Not implemented in StoreReadWithStubs - */ - function registerTable(bytes32, FieldLayout, Schema, Schema, string[] calldata, string[] calldata) public virtual { - revert StoreReadWithStubs_NotImplemented(); - } - - /** - * Not implemented in StoreReadWithStubs - */ - function setRecord(bytes32, bytes32[] calldata, bytes calldata, FieldLayout) public virtual { - revert StoreReadWithStubs_NotImplemented(); - } - - /** - * Not implemented in StoreReadWithStubs - */ - function setField(bytes32, bytes32[] calldata, uint8, bytes calldata, FieldLayout) public virtual { - revert StoreReadWithStubs_NotImplemented(); - } - - /** - * Not implemented in StoreReadWithStubs - */ - function pushToField(bytes32, bytes32[] calldata, uint8, bytes calldata, FieldLayout) public virtual { - revert StoreReadWithStubs_NotImplemented(); - } - - /** - * Not implemented in StoreReadWithStubs - */ - function popFromField(bytes32, bytes32[] calldata, uint8, uint256, FieldLayout) public virtual { - revert StoreReadWithStubs_NotImplemented(); - } - - /** - * Not implemented in StoreReadWithStubs - */ - function updateInField(bytes32, bytes32[] calldata, uint8, uint256, bytes calldata, FieldLayout) public virtual { - revert StoreReadWithStubs_NotImplemented(); - } - - /** - * Not implemented in StoreReadWithStubs - */ - function registerStoreHook(bytes32, IStoreHook, uint8) public virtual { - revert StoreReadWithStubs_NotImplemented(); - } - - /** - * Not implemented in StoreReadWithStubs - */ - function unregisterStoreHook(bytes32, IStoreHook) public virtual { - revert StoreReadWithStubs_NotImplemented(); - } - - /** - * Not implemented in StoreReadWithStubs - */ - function deleteRecord(bytes32, bytes32[] calldata, FieldLayout) public virtual { - revert StoreReadWithStubs_NotImplemented(); - } - - /** - * Not implemented in StoreReadWithStubs - */ - function emitEphemeralRecord(bytes32, bytes32[] calldata, bytes calldata, FieldLayout) public virtual { - revert StoreReadWithStubs_NotImplemented(); - } -} diff --git a/packages/store/test/KeyEncoding.t.sol b/packages/store/test/KeyEncoding.t.sol index a697813d28..1c959ba75b 100644 --- a/packages/store/test/KeyEncoding.t.sol +++ b/packages/store/test/KeyEncoding.t.sol @@ -6,11 +6,11 @@ import { GasReporter } from "@latticexyz/gas-report/src/GasReporter.sol"; import { KeyEncoding, KeyEncodingTableId } from "../src/codegen/Tables.sol"; import { ExampleEnum } from "../src/codegen/Types.sol"; import { StoreCore } from "../src/StoreCore.sol"; -import { StoreReadWithStubs } from "../src/StoreReadWithStubs.sol"; +import { StoreMock } from "../test/StoreMock.sol"; import { FieldLayout } from "../src/FieldLayout.sol"; import { Schema } from "../src/Schema.sol"; -contract KeyEncodingTest is Test, GasReporter, StoreReadWithStubs { +contract KeyEncodingTest is Test, GasReporter, StoreMock { function testRegisterAndGetFieldLayout() public { startGasReport("register KeyEncoding table"); KeyEncoding.register(); diff --git a/packages/store/test/Mixed.t.sol b/packages/store/test/Mixed.t.sol index 9719998447..b38f714d53 100644 --- a/packages/store/test/Mixed.t.sol +++ b/packages/store/test/Mixed.t.sol @@ -5,11 +5,11 @@ import { Test } from "forge-std/Test.sol"; import { GasReporter } from "@latticexyz/gas-report/src/GasReporter.sol"; import { Mixed, MixedData, MixedTableId } from "../src/codegen/Tables.sol"; import { StoreCore } from "../src/StoreCore.sol"; -import { StoreReadWithStubs } from "../src/StoreReadWithStubs.sol"; +import { StoreMock } from "../test/StoreMock.sol"; import { FieldLayout } from "../src/FieldLayout.sol"; import { Schema } from "../src/Schema.sol"; -contract MixedTest is Test, GasReporter, StoreReadWithStubs { +contract MixedTest is Test, GasReporter, StoreMock { MixedData private testMixed; function testRegisterAndGetFieldLayout() public { diff --git a/packages/store/test/StoreCore.t.sol b/packages/store/test/StoreCore.t.sol index c0b9f703be..15892cb85c 100644 --- a/packages/store/test/StoreCore.t.sol +++ b/packages/store/test/StoreCore.t.sol @@ -10,7 +10,7 @@ import { EncodeArray } from "../src/tightcoder/EncodeArray.sol"; import { FieldLayout } from "../src/FieldLayout.sol"; import { Schema } from "../src/Schema.sol"; import { PackedCounter, PackedCounterLib } from "../src/PackedCounter.sol"; -import { StoreReadWithStubs } from "../src/StoreReadWithStubs.sol"; +import { StoreMock } from "../test/StoreMock.sol"; import { IStoreErrors } from "../src/IStoreErrors.sol"; import { IStore } from "../src/IStore.sol"; import { StoreSwitch } from "../src/StoreSwitch.sol"; diff --git a/packages/store/test/StoreCoreDynamic.t.sol b/packages/store/test/StoreCoreDynamic.t.sol index 00de8cd813..19a08dd88c 100644 --- a/packages/store/test/StoreCoreDynamic.t.sol +++ b/packages/store/test/StoreCoreDynamic.t.sol @@ -9,11 +9,11 @@ import { SliceLib } from "../src/Slice.sol"; import { EncodeArray } from "../src/tightcoder/EncodeArray.sol"; import { FieldLayout } from "../src/FieldLayout.sol"; import { Schema } from "../src/Schema.sol"; -import { StoreReadWithStubs } from "../src/StoreReadWithStubs.sol"; +import { StoreMock } from "../test/StoreMock.sol"; import { FieldLayoutEncodeHelper } from "./FieldLayoutEncodeHelper.sol"; import { SchemaEncodeHelper } from "./SchemaEncodeHelper.sol"; -contract StoreCoreDynamicTest is Test, GasReporter, StoreReadWithStubs { +contract StoreCoreDynamicTest is Test, GasReporter, StoreMock { Schema internal defaultKeySchema = SchemaEncodeHelper.encode(SchemaType.BYTES32); bytes32[] internal _key; diff --git a/packages/store/test/StoreCoreGas.t.sol b/packages/store/test/StoreCoreGas.t.sol index ba47a49fe8..97e2ec65a6 100644 --- a/packages/store/test/StoreCoreGas.t.sol +++ b/packages/store/test/StoreCoreGas.t.sol @@ -11,7 +11,7 @@ import { EncodeArray } from "../src/tightcoder/EncodeArray.sol"; import { FieldLayout } from "../src/FieldLayout.sol"; import { Schema } from "../src/Schema.sol"; import { PackedCounter, PackedCounterLib } from "../src/PackedCounter.sol"; -import { StoreReadWithStubs } from "../src/StoreReadWithStubs.sol"; +import { StoreMock } from "../test/StoreMock.sol"; import { IStoreErrors } from "../src/IStoreErrors.sol"; import { IStore } from "../src/IStore.sol"; import { FieldLayoutEncodeHelper } from "./FieldLayoutEncodeHelper.sol"; diff --git a/packages/store/test/StoreMock.sol b/packages/store/test/StoreMock.sol index c0535f6368..8338942a8a 100644 --- a/packages/store/test/StoreMock.sol +++ b/packages/store/test/StoreMock.sol @@ -11,8 +11,18 @@ import { StoreRead } from "../src/StoreRead.sol"; * StoreMock is a contract wrapper around the StoreCore library for testing purposes. */ contract StoreMock is IStore, StoreRead { + constructor() { + StoreCore.initialize(); + StoreCore.registerCoreTables(); + } + // Set full record (including full dynamic data) - function setRecord(bytes32 table, bytes32[] calldata key, bytes calldata data, FieldLayout fieldLayout) public { + function setRecord( + bytes32 table, + bytes32[] calldata key, + bytes calldata data, + FieldLayout fieldLayout + ) public virtual { StoreCore.setRecord(table, key, data, fieldLayout); } @@ -23,7 +33,7 @@ contract StoreMock is IStore, StoreRead { uint8 schemaIndex, bytes calldata data, FieldLayout fieldLayout - ) public { + ) public virtual { StoreCore.setField(table, key, schemaIndex, data, fieldLayout); } @@ -34,7 +44,7 @@ contract StoreMock is IStore, StoreRead { uint8 schemaIndex, bytes calldata dataToPush, FieldLayout fieldLayout - ) public { + ) public virtual { StoreCore.pushToField(table, key, schemaIndex, dataToPush, fieldLayout); } @@ -45,7 +55,7 @@ contract StoreMock is IStore, StoreRead { uint8 schemaIndex, uint256 byteLengthToPop, FieldLayout fieldLayout - ) public { + ) public virtual { StoreCore.popFromField(table, key, schemaIndex, byteLengthToPop, fieldLayout); } @@ -57,12 +67,12 @@ contract StoreMock is IStore, StoreRead { uint256 startByteIndex, bytes calldata dataToSet, FieldLayout fieldLayout - ) public { + ) public virtual { StoreCore.updateInField(table, key, schemaIndex, startByteIndex, dataToSet, fieldLayout); } // Set full record (including full dynamic data) - function deleteRecord(bytes32 table, bytes32[] memory key, FieldLayout fieldLayout) public { + function deleteRecord(bytes32 table, bytes32[] memory key, FieldLayout fieldLayout) public virtual { StoreCore.deleteRecord(table, key, fieldLayout); } @@ -72,7 +82,7 @@ contract StoreMock is IStore, StoreRead { bytes32[] calldata key, bytes calldata data, FieldLayout fieldLayout - ) public { + ) public virtual { StoreCore.emitEphemeralRecord(table, key, data, fieldLayout); } @@ -83,17 +93,17 @@ contract StoreMock is IStore, StoreRead { Schema valueSchema, string[] calldata keyNames, string[] calldata fieldNames - ) public { + ) public virtual { StoreCore.registerTable(table, fieldLayout, keySchema, valueSchema, keyNames, fieldNames); } // Register hook to be called when a record or field is set or deleted - function registerStoreHook(bytes32 table, IStoreHook hookAddress, uint8 enabledHooksBitmap) public { + function registerStoreHook(bytes32 table, IStoreHook hookAddress, uint8 enabledHooksBitmap) public virtual { StoreCore.registerStoreHook(table, hookAddress, enabledHooksBitmap); } // Unregister hook to be called when a record or field is set or deleted - function unregisterStoreHook(bytes32 table, IStoreHook hookAddress) public { + function unregisterStoreHook(bytes32 table, IStoreHook hookAddress) public virtual { StoreCore.unregisterStoreHook(table, hookAddress); } } diff --git a/packages/store/test/StoreSwitch.t.sol b/packages/store/test/StoreSwitch.t.sol index 6ed176daee..0b6f70fb53 100644 --- a/packages/store/test/StoreSwitch.t.sol +++ b/packages/store/test/StoreSwitch.t.sol @@ -4,11 +4,11 @@ pragma solidity >=0.8.0; import { Test } from "forge-std/Test.sol"; import { GasReporter } from "@latticexyz/gas-report/src/GasReporter.sol"; import { StoreCore } from "../src/StoreCore.sol"; -import { StoreReadWithStubs } from "../src/StoreReadWithStubs.sol"; +import { StoreMock } from "../test/StoreMock.sol"; import { StoreSwitch } from "../src/StoreSwitch.sol"; // Mock Store to call MockSystem -contract StoreSwitchTestStore is StoreReadWithStubs { +contract StoreSwitchTestStore is StoreMock { MockSystem mockSystem = new MockSystem(); function callViaDelegateCall() public returns (address storeAddress) { diff --git a/packages/store/test/Vector2.t.sol b/packages/store/test/Vector2.t.sol index d834b28971..769054a76a 100644 --- a/packages/store/test/Vector2.t.sol +++ b/packages/store/test/Vector2.t.sol @@ -5,11 +5,11 @@ import { Test } from "forge-std/Test.sol"; import { GasReporter } from "@latticexyz/gas-report/src/GasReporter.sol"; import { Vector2, Vector2Data, Vector2TableId } from "../src/codegen/Tables.sol"; import { StoreCore } from "../src/StoreCore.sol"; -import { StoreReadWithStubs } from "../src/StoreReadWithStubs.sol"; +import { StoreMock } from "../test/StoreMock.sol"; import { FieldLayout } from "../src/FieldLayout.sol"; import { Schema } from "../src/Schema.sol"; -contract Vector2Test is Test, GasReporter, StoreReadWithStubs { +contract Vector2Test is Test, GasReporter, StoreMock { function testRegisterAndGetFieldLayout() public { startGasReport("register Vector2 field layout"); Vector2.register(); diff --git a/packages/store/test/tables/Callbacks.t.sol b/packages/store/test/tables/Callbacks.t.sol index 3213bdb3ee..dacf396c31 100644 --- a/packages/store/test/tables/Callbacks.t.sol +++ b/packages/store/test/tables/Callbacks.t.sol @@ -3,10 +3,10 @@ pragma solidity >=0.8.0; import { Test } from "forge-std/Test.sol"; import { GasReporter } from "@latticexyz/gas-report/src/GasReporter.sol"; -import { StoreReadWithStubs } from "../../src/StoreReadWithStubs.sol"; +import { StoreMock } from "../../test/StoreMock.sol"; import { Callbacks } from "../../src/codegen/Tables.sol"; -contract CallbacksTest is Test, GasReporter, StoreReadWithStubs { +contract CallbacksTest is Test, GasReporter, StoreMock { function testSetAndGet() public { Callbacks.register(); bytes32 key = keccak256("somekey"); diff --git a/packages/store/test/tables/StoreHooks.t.sol b/packages/store/test/tables/StoreHooks.t.sol index 05d6413351..ff347c9b86 100644 --- a/packages/store/test/tables/StoreHooks.t.sol +++ b/packages/store/test/tables/StoreHooks.t.sol @@ -3,10 +3,10 @@ pragma solidity >=0.8.0; import { Test } from "forge-std/Test.sol"; import { GasReporter } from "@latticexyz/gas-report/src/GasReporter.sol"; -import { StoreReadWithStubs } from "../../src/StoreReadWithStubs.sol"; +import { StoreMock } from "../../test/StoreMock.sol"; import { StoreHooks } from "../../src/codegen/Tables.sol"; -contract StoreHooksTest is Test, GasReporter, StoreReadWithStubs { +contract StoreHooksTest is Test, GasReporter, StoreMock { function testTable() public { // StoreHooks schema is already registered by StoreCore bytes32 key = keccak256("somekey"); diff --git a/packages/store/test/tables/StoreHooksColdLoad.t.sol b/packages/store/test/tables/StoreHooksColdLoad.t.sol index fc869bcaee..07513c2c54 100644 --- a/packages/store/test/tables/StoreHooksColdLoad.t.sol +++ b/packages/store/test/tables/StoreHooksColdLoad.t.sol @@ -3,10 +3,10 @@ pragma solidity >=0.8.0; import { Test } from "forge-std/Test.sol"; import { GasReporter } from "@latticexyz/gas-report/src/GasReporter.sol"; -import { StoreReadWithStubs } from "../../src/StoreReadWithStubs.sol"; +import { StoreMock } from "../../test/StoreMock.sol"; import { StoreHooks } from "../../src/codegen/Tables.sol"; -contract StoreHooksColdLoadTest is Test, GasReporter, StoreReadWithStubs { +contract StoreHooksColdLoadTest is Test, GasReporter, StoreMock { bytes21[] hooks; function setUp() public { diff --git a/packages/world/abi/BalanceTransferSystem.sol/BalanceTransferSystem.abi.json b/packages/world/abi/BalanceTransferSystem.sol/BalanceTransferSystem.abi.json index 6053b3e357..5837ac41b0 100644 --- a/packages/world/abi/BalanceTransferSystem.sol/BalanceTransferSystem.abi.json +++ b/packages/world/abi/BalanceTransferSystem.sol/BalanceTransferSystem.abi.json @@ -117,17 +117,6 @@ "name": "InvalidSelector", "type": "error" }, - { - "inputs": [ - { - "internalType": "string", - "name": "module", - "type": "string" - } - ], - "name": "ModuleAlreadyInstalled", - "type": "error" - }, { "inputs": [ { @@ -209,6 +198,11 @@ "name": "SystemExists", "type": "error" }, + { + "inputs": [], + "name": "WorldAlreadyInitialized", + "type": "error" + }, { "inputs": [], "name": "_msgSender", diff --git a/packages/world/abi/BalanceTransferSystem.sol/BalanceTransferSystem.abi.json.d.ts b/packages/world/abi/BalanceTransferSystem.sol/BalanceTransferSystem.abi.json.d.ts index fc9e3b5c12..35b1726fb1 100644 --- a/packages/world/abi/BalanceTransferSystem.sol/BalanceTransferSystem.abi.json.d.ts +++ b/packages/world/abi/BalanceTransferSystem.sol/BalanceTransferSystem.abi.json.d.ts @@ -117,17 +117,6 @@ declare const abi: [ name: "InvalidSelector"; type: "error"; }, - { - inputs: [ - { - internalType: "string"; - name: "module"; - type: "string"; - } - ]; - name: "ModuleAlreadyInstalled"; - type: "error"; - }, { inputs: [ { @@ -209,6 +198,11 @@ declare const abi: [ name: "SystemExists"; type: "error"; }, + { + inputs: []; + name: "WorldAlreadyInitialized"; + type: "error"; + }, { inputs: []; name: "_msgSender"; diff --git a/packages/world/abi/CoreSystem.sol/CoreSystem.abi.json b/packages/world/abi/CoreSystem.sol/CoreSystem.abi.json index 3a730bca65..37b796f074 100644 --- a/packages/world/abi/CoreSystem.sol/CoreSystem.abi.json +++ b/packages/world/abi/CoreSystem.sol/CoreSystem.abi.json @@ -117,17 +117,6 @@ "name": "InvalidSelector", "type": "error" }, - { - "inputs": [ - { - "internalType": "string", - "name": "module", - "type": "string" - } - ], - "name": "ModuleAlreadyInstalled", - "type": "error" - }, { "inputs": [ { @@ -294,6 +283,11 @@ "name": "SystemExists", "type": "error" }, + { + "inputs": [], + "name": "WorldAlreadyInitialized", + "type": "error" + }, { "anonymous": false, "inputs": [ diff --git a/packages/world/abi/CoreSystem.sol/CoreSystem.abi.json.d.ts b/packages/world/abi/CoreSystem.sol/CoreSystem.abi.json.d.ts index 1991fd11ad..4f7a99f85c 100644 --- a/packages/world/abi/CoreSystem.sol/CoreSystem.abi.json.d.ts +++ b/packages/world/abi/CoreSystem.sol/CoreSystem.abi.json.d.ts @@ -117,17 +117,6 @@ declare const abi: [ name: "InvalidSelector"; type: "error"; }, - { - inputs: [ - { - internalType: "string"; - name: "module"; - type: "string"; - } - ]; - name: "ModuleAlreadyInstalled"; - type: "error"; - }, { inputs: [ { @@ -294,6 +283,11 @@ declare const abi: [ name: "SystemExists"; type: "error"; }, + { + inputs: []; + name: "WorldAlreadyInitialized"; + type: "error"; + }, { anonymous: false; inputs: [ diff --git a/packages/world/abi/IBaseWorld.sol/IBaseWorld.abi.json b/packages/world/abi/IBaseWorld.sol/IBaseWorld.abi.json index 7cb990eefd..89c084071c 100644 --- a/packages/world/abi/IBaseWorld.sol/IBaseWorld.abi.json +++ b/packages/world/abi/IBaseWorld.sol/IBaseWorld.abi.json @@ -96,17 +96,6 @@ "name": "InvalidSelector", "type": "error" }, - { - "inputs": [ - { - "internalType": "string", - "name": "module", - "type": "string" - } - ], - "name": "ModuleAlreadyInstalled", - "type": "error" - }, { "inputs": [ { @@ -262,6 +251,11 @@ "name": "SystemExists", "type": "error" }, + { + "inputs": [], + "name": "WorldAlreadyInitialized", + "type": "error" + }, { "anonymous": false, "inputs": [], @@ -421,6 +415,19 @@ "stateMutability": "payable", "type": "function" }, + { + "inputs": [], + "name": "creator", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -688,6 +695,19 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "contract IModule", + "name": "coreModule", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { diff --git a/packages/world/abi/IBaseWorld.sol/IBaseWorld.abi.json.d.ts b/packages/world/abi/IBaseWorld.sol/IBaseWorld.abi.json.d.ts index a1b710dd0a..de47b3b5e6 100644 --- a/packages/world/abi/IBaseWorld.sol/IBaseWorld.abi.json.d.ts +++ b/packages/world/abi/IBaseWorld.sol/IBaseWorld.abi.json.d.ts @@ -96,17 +96,6 @@ declare const abi: [ name: "InvalidSelector"; type: "error"; }, - { - inputs: [ - { - internalType: "string"; - name: "module"; - type: "string"; - } - ]; - name: "ModuleAlreadyInstalled"; - type: "error"; - }, { inputs: [ { @@ -262,6 +251,11 @@ declare const abi: [ name: "SystemExists"; type: "error"; }, + { + inputs: []; + name: "WorldAlreadyInitialized"; + type: "error"; + }, { anonymous: false; inputs: []; @@ -421,6 +415,19 @@ declare const abi: [ stateMutability: "payable"; type: "function"; }, + { + inputs: []; + name: "creator"; + outputs: [ + { + internalType: "address"; + name: ""; + type: "address"; + } + ]; + stateMutability: "view"; + type: "function"; + }, { inputs: [ { @@ -688,6 +695,19 @@ declare const abi: [ stateMutability: "nonpayable"; type: "function"; }, + { + inputs: [ + { + internalType: "contract IModule"; + name: "coreModule"; + type: "address"; + } + ]; + name: "initialize"; + outputs: []; + stateMutability: "nonpayable"; + type: "function"; + }, { inputs: [ { diff --git a/packages/world/abi/IWorldErrors.sol/IWorldErrors.abi.json b/packages/world/abi/IWorldErrors.sol/IWorldErrors.abi.json index 0af520372a..06f59dc6e9 100644 --- a/packages/world/abi/IWorldErrors.sol/IWorldErrors.abi.json +++ b/packages/world/abi/IWorldErrors.sol/IWorldErrors.abi.json @@ -96,17 +96,6 @@ "name": "InvalidSelector", "type": "error" }, - { - "inputs": [ - { - "internalType": "string", - "name": "module", - "type": "string" - } - ], - "name": "ModuleAlreadyInstalled", - "type": "error" - }, { "inputs": [ { @@ -139,5 +128,10 @@ ], "name": "SystemExists", "type": "error" + }, + { + "inputs": [], + "name": "WorldAlreadyInitialized", + "type": "error" } ] \ No newline at end of file diff --git a/packages/world/abi/IWorldErrors.sol/IWorldErrors.abi.json.d.ts b/packages/world/abi/IWorldErrors.sol/IWorldErrors.abi.json.d.ts index 476da1d71c..1dceca5afa 100644 --- a/packages/world/abi/IWorldErrors.sol/IWorldErrors.abi.json.d.ts +++ b/packages/world/abi/IWorldErrors.sol/IWorldErrors.abi.json.d.ts @@ -96,17 +96,6 @@ declare const abi: [ name: "InvalidSelector"; type: "error"; }, - { - inputs: [ - { - internalType: "string"; - name: "module"; - type: "string"; - } - ]; - name: "ModuleAlreadyInstalled"; - type: "error"; - }, { inputs: [ { @@ -139,6 +128,11 @@ declare const abi: [ ]; name: "SystemExists"; type: "error"; + }, + { + inputs: []; + name: "WorldAlreadyInitialized"; + type: "error"; } ]; export default abi; diff --git a/packages/world/abi/IWorldKernel.sol/IWorldKernel.abi.json b/packages/world/abi/IWorldKernel.sol/IWorldKernel.abi.json index 5c7a2545d7..d4adfc2183 100644 --- a/packages/world/abi/IWorldKernel.sol/IWorldKernel.abi.json +++ b/packages/world/abi/IWorldKernel.sol/IWorldKernel.abi.json @@ -96,17 +96,6 @@ "name": "InvalidSelector", "type": "error" }, - { - "inputs": [ - { - "internalType": "string", - "name": "module", - "type": "string" - } - ], - "name": "ModuleAlreadyInstalled", - "type": "error" - }, { "inputs": [ { @@ -140,6 +129,11 @@ "name": "SystemExists", "type": "error" }, + { + "inputs": [], + "name": "WorldAlreadyInitialized", + "type": "error" + }, { "anonymous": false, "inputs": [], @@ -199,6 +193,32 @@ "stateMutability": "payable", "type": "function" }, + { + "inputs": [], + "name": "creator", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IModule", + "name": "coreModule", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { diff --git a/packages/world/abi/IWorldKernel.sol/IWorldKernel.abi.json.d.ts b/packages/world/abi/IWorldKernel.sol/IWorldKernel.abi.json.d.ts index f6f89bf906..69abecf3f3 100644 --- a/packages/world/abi/IWorldKernel.sol/IWorldKernel.abi.json.d.ts +++ b/packages/world/abi/IWorldKernel.sol/IWorldKernel.abi.json.d.ts @@ -96,17 +96,6 @@ declare const abi: [ name: "InvalidSelector"; type: "error"; }, - { - inputs: [ - { - internalType: "string"; - name: "module"; - type: "string"; - } - ]; - name: "ModuleAlreadyInstalled"; - type: "error"; - }, { inputs: [ { @@ -140,6 +129,11 @@ declare const abi: [ name: "SystemExists"; type: "error"; }, + { + inputs: []; + name: "WorldAlreadyInitialized"; + type: "error"; + }, { anonymous: false; inputs: []; @@ -199,6 +193,32 @@ declare const abi: [ stateMutability: "payable"; type: "function"; }, + { + inputs: []; + name: "creator"; + outputs: [ + { + internalType: "address"; + name: ""; + type: "address"; + } + ]; + stateMutability: "view"; + type: "function"; + }, + { + inputs: [ + { + internalType: "contract IModule"; + name: "coreModule"; + type: "address"; + } + ]; + name: "initialize"; + outputs: []; + stateMutability: "nonpayable"; + type: "function"; + }, { inputs: [ { diff --git a/packages/world/abi/StoreRead.sol/StoreRead.abi.json b/packages/world/abi/StoreRead.sol/StoreRead.abi.json index 1f8dd917db..d667734d9d 100644 --- a/packages/world/abi/StoreRead.sol/StoreRead.abi.json +++ b/packages/world/abi/StoreRead.sol/StoreRead.abi.json @@ -1,9 +1,4 @@ [ - { - "inputs": [], - "stateMutability": "nonpayable", - "type": "constructor" - }, { "inputs": [ { @@ -25,128 +20,11 @@ "name": "FieldLayoutLib_StaticLengthIsZero", "type": "error" }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "length", - "type": "uint256" - } - ], - "name": "SchemaLib_InvalidLength", - "type": "error" - }, - { - "inputs": [], - "name": "SchemaLib_StaticTypeAfterDynamicType", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - }, - { - "internalType": "uint256", - "name": "start", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "end", - "type": "uint256" - } - ], - "name": "Slice_OutOfBounds", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "expected", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "received", - "type": "uint256" - } - ], - "name": "StoreCore_InvalidDataLength", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "expected", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "received", - "type": "uint256" - } - ], - "name": "StoreCore_InvalidFieldNamesLength", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "expected", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "received", - "type": "uint256" - } - ], - "name": "StoreCore_InvalidKeyNamesLength", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "expected", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "received", - "type": "uint256" - } - ], - "name": "StoreCore_InvalidValueSchemaLength", - "type": "error" - }, { "inputs": [], "name": "StoreCore_NotDynamicField", "type": "error" }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "tableId", - "type": "bytes32" - }, - { - "internalType": "string", - "name": "tableIdString", - "type": "string" - } - ], - "name": "StoreCore_TableAlreadyExists", - "type": "error" - }, { "inputs": [ { diff --git a/packages/world/abi/StoreRead.sol/StoreRead.abi.json.d.ts b/packages/world/abi/StoreRead.sol/StoreRead.abi.json.d.ts index 677134cc99..b55d3b54f0 100644 --- a/packages/world/abi/StoreRead.sol/StoreRead.abi.json.d.ts +++ b/packages/world/abi/StoreRead.sol/StoreRead.abi.json.d.ts @@ -1,9 +1,4 @@ declare const abi: [ - { - inputs: []; - stateMutability: "nonpayable"; - type: "constructor"; - }, { inputs: [ { @@ -25,128 +20,11 @@ declare const abi: [ name: "FieldLayoutLib_StaticLengthIsZero"; type: "error"; }, - { - inputs: [ - { - internalType: "uint256"; - name: "length"; - type: "uint256"; - } - ]; - name: "SchemaLib_InvalidLength"; - type: "error"; - }, - { - inputs: []; - name: "SchemaLib_StaticTypeAfterDynamicType"; - type: "error"; - }, - { - inputs: [ - { - internalType: "bytes"; - name: "data"; - type: "bytes"; - }, - { - internalType: "uint256"; - name: "start"; - type: "uint256"; - }, - { - internalType: "uint256"; - name: "end"; - type: "uint256"; - } - ]; - name: "Slice_OutOfBounds"; - type: "error"; - }, - { - inputs: [ - { - internalType: "uint256"; - name: "expected"; - type: "uint256"; - }, - { - internalType: "uint256"; - name: "received"; - type: "uint256"; - } - ]; - name: "StoreCore_InvalidDataLength"; - type: "error"; - }, - { - inputs: [ - { - internalType: "uint256"; - name: "expected"; - type: "uint256"; - }, - { - internalType: "uint256"; - name: "received"; - type: "uint256"; - } - ]; - name: "StoreCore_InvalidFieldNamesLength"; - type: "error"; - }, - { - inputs: [ - { - internalType: "uint256"; - name: "expected"; - type: "uint256"; - }, - { - internalType: "uint256"; - name: "received"; - type: "uint256"; - } - ]; - name: "StoreCore_InvalidKeyNamesLength"; - type: "error"; - }, - { - inputs: [ - { - internalType: "uint256"; - name: "expected"; - type: "uint256"; - }, - { - internalType: "uint256"; - name: "received"; - type: "uint256"; - } - ]; - name: "StoreCore_InvalidValueSchemaLength"; - type: "error"; - }, { inputs: []; name: "StoreCore_NotDynamicField"; type: "error"; }, - { - inputs: [ - { - internalType: "bytes32"; - name: "tableId"; - type: "bytes32"; - }, - { - internalType: "string"; - name: "tableIdString"; - type: "string"; - } - ]; - name: "StoreCore_TableAlreadyExists"; - type: "error"; - }, { inputs: [ { diff --git a/packages/world/abi/StoreRegistrationSystem.sol/StoreRegistrationSystem.abi.json b/packages/world/abi/StoreRegistrationSystem.sol/StoreRegistrationSystem.abi.json index d521da57d5..bf33e46ef0 100644 --- a/packages/world/abi/StoreRegistrationSystem.sol/StoreRegistrationSystem.abi.json +++ b/packages/world/abi/StoreRegistrationSystem.sol/StoreRegistrationSystem.abi.json @@ -117,17 +117,6 @@ "name": "InvalidSelector", "type": "error" }, - { - "inputs": [ - { - "internalType": "string", - "name": "module", - "type": "string" - } - ], - "name": "ModuleAlreadyInstalled", - "type": "error" - }, { "inputs": [ { @@ -294,6 +283,11 @@ "name": "SystemExists", "type": "error" }, + { + "inputs": [], + "name": "WorldAlreadyInitialized", + "type": "error" + }, { "inputs": [], "name": "_msgSender", diff --git a/packages/world/abi/StoreRegistrationSystem.sol/StoreRegistrationSystem.abi.json.d.ts b/packages/world/abi/StoreRegistrationSystem.sol/StoreRegistrationSystem.abi.json.d.ts index bb4ce29c6d..d09a81812a 100644 --- a/packages/world/abi/StoreRegistrationSystem.sol/StoreRegistrationSystem.abi.json.d.ts +++ b/packages/world/abi/StoreRegistrationSystem.sol/StoreRegistrationSystem.abi.json.d.ts @@ -117,17 +117,6 @@ declare const abi: [ name: "InvalidSelector"; type: "error"; }, - { - inputs: [ - { - internalType: "string"; - name: "module"; - type: "string"; - } - ]; - name: "ModuleAlreadyInstalled"; - type: "error"; - }, { inputs: [ { @@ -294,6 +283,11 @@ declare const abi: [ name: "SystemExists"; type: "error"; }, + { + inputs: []; + name: "WorldAlreadyInitialized"; + type: "error"; + }, { inputs: []; name: "_msgSender"; diff --git a/packages/world/abi/World.sol/World.abi.json b/packages/world/abi/World.sol/World.abi.json index e6f9da0703..f5e0c9531a 100644 --- a/packages/world/abi/World.sol/World.abi.json +++ b/packages/world/abi/World.sol/World.abi.json @@ -122,17 +122,6 @@ "name": "InvalidSelector", "type": "error" }, - { - "inputs": [ - { - "internalType": "string", - "name": "module", - "type": "string" - } - ], - "name": "ModuleAlreadyInstalled", - "type": "error" - }, { "inputs": [ { @@ -166,22 +155,6 @@ "name": "ResourceNotFound", "type": "error" }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "length", - "type": "uint256" - } - ], - "name": "SchemaLib_InvalidLength", - "type": "error" - }, - { - "inputs": [], - "name": "SchemaLib_StaticTypeAfterDynamicType", - "type": "error" - }, { "inputs": [ { @@ -235,75 +208,11 @@ "name": "StoreCore_InvalidDataLength", "type": "error" }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "expected", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "received", - "type": "uint256" - } - ], - "name": "StoreCore_InvalidFieldNamesLength", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "expected", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "received", - "type": "uint256" - } - ], - "name": "StoreCore_InvalidKeyNamesLength", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "expected", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "received", - "type": "uint256" - } - ], - "name": "StoreCore_InvalidValueSchemaLength", - "type": "error" - }, { "inputs": [], "name": "StoreCore_NotDynamicField", "type": "error" }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "tableId", - "type": "bytes32" - }, - { - "internalType": "string", - "name": "tableIdString", - "type": "string" - } - ], - "name": "StoreCore_TableAlreadyExists", - "type": "error" - }, { "inputs": [ { @@ -331,6 +240,11 @@ "name": "SystemExists", "type": "error" }, + { + "inputs": [], + "name": "WorldAlreadyInitialized", + "type": "error" + }, { "anonymous": false, "inputs": [], @@ -469,6 +383,19 @@ "stateMutability": "payable", "type": "function" }, + { + "inputs": [], + "name": "creator", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -690,6 +617,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "contract IModule", + "name": "coreModule", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { diff --git a/packages/world/abi/World.sol/World.abi.json.d.ts b/packages/world/abi/World.sol/World.abi.json.d.ts index 733e1d2b57..63c17a18b9 100644 --- a/packages/world/abi/World.sol/World.abi.json.d.ts +++ b/packages/world/abi/World.sol/World.abi.json.d.ts @@ -122,17 +122,6 @@ declare const abi: [ name: "InvalidSelector"; type: "error"; }, - { - inputs: [ - { - internalType: "string"; - name: "module"; - type: "string"; - } - ]; - name: "ModuleAlreadyInstalled"; - type: "error"; - }, { inputs: [ { @@ -166,22 +155,6 @@ declare const abi: [ name: "ResourceNotFound"; type: "error"; }, - { - inputs: [ - { - internalType: "uint256"; - name: "length"; - type: "uint256"; - } - ]; - name: "SchemaLib_InvalidLength"; - type: "error"; - }, - { - inputs: []; - name: "SchemaLib_StaticTypeAfterDynamicType"; - type: "error"; - }, { inputs: [ { @@ -235,75 +208,11 @@ declare const abi: [ name: "StoreCore_InvalidDataLength"; type: "error"; }, - { - inputs: [ - { - internalType: "uint256"; - name: "expected"; - type: "uint256"; - }, - { - internalType: "uint256"; - name: "received"; - type: "uint256"; - } - ]; - name: "StoreCore_InvalidFieldNamesLength"; - type: "error"; - }, - { - inputs: [ - { - internalType: "uint256"; - name: "expected"; - type: "uint256"; - }, - { - internalType: "uint256"; - name: "received"; - type: "uint256"; - } - ]; - name: "StoreCore_InvalidKeyNamesLength"; - type: "error"; - }, - { - inputs: [ - { - internalType: "uint256"; - name: "expected"; - type: "uint256"; - }, - { - internalType: "uint256"; - name: "received"; - type: "uint256"; - } - ]; - name: "StoreCore_InvalidValueSchemaLength"; - type: "error"; - }, { inputs: []; name: "StoreCore_NotDynamicField"; type: "error"; }, - { - inputs: [ - { - internalType: "bytes32"; - name: "tableId"; - type: "bytes32"; - }, - { - internalType: "string"; - name: "tableIdString"; - type: "string"; - } - ]; - name: "StoreCore_TableAlreadyExists"; - type: "error"; - }, { inputs: [ { @@ -331,6 +240,11 @@ declare const abi: [ name: "SystemExists"; type: "error"; }, + { + inputs: []; + name: "WorldAlreadyInitialized"; + type: "error"; + }, { anonymous: false; inputs: []; @@ -469,6 +383,19 @@ declare const abi: [ stateMutability: "payable"; type: "function"; }, + { + inputs: []; + name: "creator"; + outputs: [ + { + internalType: "address"; + name: ""; + type: "address"; + } + ]; + stateMutability: "view"; + type: "function"; + }, { inputs: [ { @@ -690,6 +617,19 @@ declare const abi: [ stateMutability: "view"; type: "function"; }, + { + inputs: [ + { + internalType: "contract IModule"; + name: "coreModule"; + type: "address"; + } + ]; + name: "initialize"; + outputs: []; + stateMutability: "nonpayable"; + type: "function"; + }, { inputs: [ { diff --git a/packages/world/abi/WorldRegistrationSystem.sol/WorldRegistrationSystem.abi.json b/packages/world/abi/WorldRegistrationSystem.sol/WorldRegistrationSystem.abi.json index 8a3634a6da..c06b81b68a 100644 --- a/packages/world/abi/WorldRegistrationSystem.sol/WorldRegistrationSystem.abi.json +++ b/packages/world/abi/WorldRegistrationSystem.sol/WorldRegistrationSystem.abi.json @@ -117,17 +117,6 @@ "name": "InvalidSelector", "type": "error" }, - { - "inputs": [ - { - "internalType": "string", - "name": "module", - "type": "string" - } - ], - "name": "ModuleAlreadyInstalled", - "type": "error" - }, { "inputs": [ { @@ -214,6 +203,11 @@ "name": "SystemExists", "type": "error" }, + { + "inputs": [], + "name": "WorldAlreadyInitialized", + "type": "error" + }, { "inputs": [], "name": "_msgSender", diff --git a/packages/world/abi/WorldRegistrationSystem.sol/WorldRegistrationSystem.abi.json.d.ts b/packages/world/abi/WorldRegistrationSystem.sol/WorldRegistrationSystem.abi.json.d.ts index a6e9822f2f..297a309951 100644 --- a/packages/world/abi/WorldRegistrationSystem.sol/WorldRegistrationSystem.abi.json.d.ts +++ b/packages/world/abi/WorldRegistrationSystem.sol/WorldRegistrationSystem.abi.json.d.ts @@ -117,17 +117,6 @@ declare const abi: [ name: "InvalidSelector"; type: "error"; }, - { - inputs: [ - { - internalType: "string"; - name: "module"; - type: "string"; - } - ]; - name: "ModuleAlreadyInstalled"; - type: "error"; - }, { inputs: [ { @@ -214,6 +203,11 @@ declare const abi: [ name: "SystemExists"; type: "error"; }, + { + inputs: []; + name: "WorldAlreadyInitialized"; + type: "error"; + }, { inputs: []; name: "_msgSender"; diff --git a/packages/world/gas-report.json b/packages/world/gas-report.json index 6ec43db51b..4157bcf039 100644 --- a/packages/world/gas-report.json +++ b/packages/world/gas-report.json @@ -39,73 +39,73 @@ "file": "test/KeysInTableModule.t.sol", "test": "testInstallComposite", "name": "install keys in table module", - "gasUsed": 1519694 + "gasUsed": 1519669 }, { "file": "test/KeysInTableModule.t.sol", "test": "testInstallGas", "name": "install keys in table module", - "gasUsed": 1519694 + "gasUsed": 1519669 }, { "file": "test/KeysInTableModule.t.sol", "test": "testInstallGas", "name": "set a record on a table with keysInTableModule installed", - "gasUsed": 183330 + "gasUsed": 183388 }, { "file": "test/KeysInTableModule.t.sol", "test": "testInstallSingleton", "name": "install keys in table module", - "gasUsed": 1519694 + "gasUsed": 1519669 }, { "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookCompositeGas", "name": "install keys in table module", - "gasUsed": 1519694 + "gasUsed": 1519669 }, { "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookCompositeGas", "name": "change a composite record on a table with keysInTableModule installed", - "gasUsed": 28471 + "gasUsed": 28498 }, { "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookCompositeGas", "name": "delete a composite record on a table with keysInTableModule installed", - "gasUsed": 251350 + "gasUsed": 251457 }, { "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookGas", "name": "install keys in table module", - "gasUsed": 1519694 + "gasUsed": 1519669 }, { "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookGas", "name": "change a record on a table with keysInTableModule installed", - "gasUsed": 27194 + "gasUsed": 27221 }, { "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookGas", "name": "delete a record on a table with keysInTableModule installed", - "gasUsed": 132776 + "gasUsed": 132823 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testGetKeysWithValueGas", "name": "install keys with value module", - "gasUsed": 728670 + "gasUsed": 728673 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testGetKeysWithValueGas", "name": "Get list of keys with a given value", - "gasUsed": 7486 + "gasUsed": 7508 }, { "file": "test/KeysWithValueModule.t.sol", @@ -117,264 +117,264 @@ "file": "test/KeysWithValueModule.t.sol", "test": "testInstall", "name": "install keys with value module", - "gasUsed": 728670 + "gasUsed": 728673 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testInstall", "name": "set a record on a table with KeysWithValueModule installed", - "gasUsed": 157267 + "gasUsed": 157348 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testSetAndDeleteRecordHook", "name": "install keys with value module", - "gasUsed": 728670 + "gasUsed": 728673 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testSetAndDeleteRecordHook", "name": "change a record on a table with KeysWithValueModule installed", - "gasUsed": 126277 + "gasUsed": 126291 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testSetAndDeleteRecordHook", "name": "delete a record on a table with KeysWithValueModule installed", - "gasUsed": 49021 + "gasUsed": 49099 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testSetField", "name": "install keys with value module", - "gasUsed": 728670 + "gasUsed": 728673 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testSetField", "name": "set a field on a table with KeysWithValueModule installed", - "gasUsed": 163755 + "gasUsed": 163792 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testSetField", "name": "change a field on a table with KeysWithValueModule installed", - "gasUsed": 128514 + "gasUsed": 128551 }, { "file": "test/query.t.sol", "test": "testCombinedHasHasValueNotQuery", "name": "CombinedHasHasValueNotQuery", - "gasUsed": 141202 + "gasUsed": 141196 }, { "file": "test/query.t.sol", "test": "testCombinedHasHasValueQuery", "name": "CombinedHasHasValueQuery", - "gasUsed": 69011 + "gasUsed": 69053 }, { "file": "test/query.t.sol", "test": "testCombinedHasNotQuery", "name": "CombinedHasNotQuery", - "gasUsed": 184270 + "gasUsed": 184150 }, { "file": "test/query.t.sol", "test": "testCombinedHasQuery", "name": "CombinedHasQuery", - "gasUsed": 122090 + "gasUsed": 121994 }, { "file": "test/query.t.sol", "test": "testCombinedHasValueNotQuery", "name": "CombinedHasValueNotQuery", - "gasUsed": 117822 + "gasUsed": 117772 }, { "file": "test/query.t.sol", "test": "testCombinedHasValueQuery", "name": "CombinedHasValueQuery", - "gasUsed": 19120 + "gasUsed": 19164 }, { "file": "test/query.t.sol", "test": "testHasQuery", "name": "HasQuery", - "gasUsed": 27974 + "gasUsed": 27950 }, { "file": "test/query.t.sol", "test": "testHasQuery1000Keys", "name": "HasQuery with 1000 keys", - "gasUsed": 7068034 + "gasUsed": 7068010 }, { "file": "test/query.t.sol", "test": "testHasQuery100Keys", "name": "HasQuery with 100 keys", - "gasUsed": 672254 + "gasUsed": 672230 }, { "file": "test/query.t.sol", "test": "testHasValueQuery", "name": "HasValueQuery", - "gasUsed": 9237 + "gasUsed": 9259 }, { "file": "test/query.t.sol", "test": "testNotValueQuery", "name": "NotValueQuery", - "gasUsed": 62610 + "gasUsed": 62652 }, { "file": "test/StandardDelegationsModule.t.sol", "test": "testCallFromCallboundDelegation", "name": "register a callbound delegation", - "gasUsed": 133339 + "gasUsed": 133325 }, { "file": "test/StandardDelegationsModule.t.sol", "test": "testCallFromCallboundDelegation", "name": "call a system via a callbound delegation", - "gasUsed": 49400 + "gasUsed": 49383 }, { "file": "test/StandardDelegationsModule.t.sol", "test": "testCallFromTimeboundDelegation", "name": "register a timebound delegation", - "gasUsed": 127695 + "gasUsed": 127681 }, { "file": "test/StandardDelegationsModule.t.sol", "test": "testCallFromTimeboundDelegation", "name": "call a system via a timebound delegation", - "gasUsed": 38491 + "gasUsed": 38474 }, { "file": "test/UniqueEntityModule.t.sol", "test": "testInstall", "name": "install unique entity module", - "gasUsed": 788009 + "gasUsed": 787953 }, { "file": "test/UniqueEntityModule.t.sol", "test": "testInstall", "name": "get a unique entity nonce (non-root module)", - "gasUsed": 67487 + "gasUsed": 67456 }, { "file": "test/UniqueEntityModule.t.sol", "test": "testInstallRoot", "name": "installRoot unique entity module", - "gasUsed": 771491 + "gasUsed": 771494 }, { "file": "test/UniqueEntityModule.t.sol", "test": "testInstallRoot", "name": "get a unique entity nonce (root module)", - "gasUsed": 67487 + "gasUsed": 67456 }, { "file": "test/World.t.sol", "test": "testCall", "name": "call a system via the World", - "gasUsed": 19578 + "gasUsed": 19564 }, { "file": "test/World.t.sol", "test": "testCallFromUnlimitedDelegation", "name": "register an unlimited delegation", - "gasUsed": 59058 + "gasUsed": 59044 }, { "file": "test/World.t.sol", "test": "testCallFromUnlimitedDelegation", "name": "call a system via an unlimited delegation", - "gasUsed": 19964 + "gasUsed": 19950 }, { "file": "test/World.t.sol", "test": "testDeleteRecord", "name": "Delete record", - "gasUsed": 13858 + "gasUsed": 13886 }, { "file": "test/World.t.sol", "test": "testPushToField", "name": "Push data to the table", - "gasUsed": 93235 + "gasUsed": 93261 }, { "file": "test/World.t.sol", "test": "testRegisterFallbackSystem", "name": "Register a fallback system", - "gasUsed": 75428 + "gasUsed": 75414 }, { "file": "test/World.t.sol", "test": "testRegisterFallbackSystem", "name": "Register a root fallback system", - "gasUsed": 68677 + "gasUsed": 68663 }, { "file": "test/World.t.sol", "test": "testRegisterFunctionSelector", "name": "Register a function selector", - "gasUsed": 96022 + "gasUsed": 96008 }, { "file": "test/World.t.sol", "test": "testRegisterNamespace", "name": "Register a new namespace", - "gasUsed": 146385 + "gasUsed": 146371 }, { "file": "test/World.t.sol", "test": "testRegisterRootFunctionSelector", "name": "Register a root function selector", - "gasUsed": 90595 + "gasUsed": 90581 }, { "file": "test/World.t.sol", "test": "testRegisterTable", "name": "Register a new table in the namespace", - "gasUsed": 685351 + "gasUsed": 685337 }, { "file": "test/World.t.sol", "test": "testSetField", "name": "Write data to a table field", - "gasUsed": 41938 + "gasUsed": 41899 }, { "file": "test/World.t.sol", "test": "testSetRecord", "name": "Write data to the table", - "gasUsed": 41383 + "gasUsed": 41344 }, { "file": "test/WorldDynamicUpdate.t.sol", "test": "testPopFromField", "name": "pop 1 address (cold)", - "gasUsed": 32936 + "gasUsed": 32940 }, { "file": "test/WorldDynamicUpdate.t.sol", "test": "testPopFromField", "name": "pop 1 address (warm)", - "gasUsed": 19725 + "gasUsed": 19729 }, { "file": "test/WorldDynamicUpdate.t.sol", "test": "testUpdateInField", "name": "updateInField 1 item (cold)", - "gasUsed": 35347 + "gasUsed": 35373 }, { "file": "test/WorldDynamicUpdate.t.sol", "test": "testUpdateInField", "name": "updateInField 1 item (warm)", - "gasUsed": 22552 + "gasUsed": 22578 } ] diff --git a/packages/world/src/World.sol b/packages/world/src/World.sol index 90859ba849..9544c61608 100644 --- a/packages/world/src/World.sol +++ b/packages/world/src/World.sol @@ -2,6 +2,7 @@ pragma solidity >=0.8.0; import { StoreRead } from "@latticexyz/store/src/StoreRead.sol"; +import { StoreCore } from "@latticexyz/store/src/StoreCore.sol"; import { IStoreData } from "@latticexyz/store/src/IStore.sol"; import { StoreCore } from "@latticexyz/store/src/StoreCore.sol"; import { Bytes } from "@latticexyz/store/src/Bytes.sol"; @@ -29,20 +30,36 @@ import { Systems } from "./modules/core/tables/Systems.sol"; import { SystemHooks } from "./modules/core/tables/SystemHooks.sol"; import { FunctionSelectors } from "./modules/core/tables/FunctionSelectors.sol"; import { Balances } from "./modules/core/tables/Balances.sol"; +import { CORE_MODULE_NAME } from "./modules/core/constants.sol"; contract World is StoreRead, IStoreData, IWorldKernel { using ResourceSelector for bytes32; + address public immutable creator; constructor() { - // Register internal NamespaceOwner table and give ownership of the root - // namespace to msg.sender. This is done in the constructor instead of a - // module, so that we can use it for access control checks in `installRootModule`. - NamespaceOwner.register(); - NamespaceOwner.set(ROOT_NAMESPACE, msg.sender); - + creator = msg.sender; + StoreCore.initialize(); emit HelloWorld(); } + /** + * Allows the creator of the World to initialize the World once. + */ + function initialize(IModule coreModule) public { + // Only the initial creator of the World can initialize it + if (msg.sender != creator) { + revert AccessDenied(ResourceSelector.from(ROOT_NAMESPACE).toString(), msg.sender); + } + + // The World can only be initialized once + if (InstalledModules.getModuleAddress(CORE_MODULE_NAME, keccak256("")) != address(0)) { + revert WorldAlreadyInitialized(); + } + + // Initialize the World by installing the core module + _installRootModule(coreModule, new bytes(0)); + } + /** * Install the given root module in the World. * Requires the caller to own the root namespace. @@ -50,7 +67,10 @@ contract World is StoreRead, IStoreData, IWorldKernel { */ function installRootModule(IModule module, bytes memory args) public { AccessControl.requireOwner(ROOT_NAMESPACE, msg.sender); + _installRootModule(module, args); + } + function _installRootModule(IModule module, bytes memory args) internal { // Require the provided address to implement the IModule interface requireInterface(address(module), MODULE_INTERFACE_ID); diff --git a/packages/world/src/factories/WorldFactory.sol b/packages/world/src/factories/WorldFactory.sol index 318d225cf5..752ade4ddb 100644 --- a/packages/world/src/factories/WorldFactory.sol +++ b/packages/world/src/factories/WorldFactory.sol @@ -26,7 +26,7 @@ contract WorldFactory is IWorldFactory { IBaseWorld world = IBaseWorld(worldAddress); // Initialize the World and transfer ownership to the caller - world.installRootModule(coreModule, new bytes(0)); + world.initialize(coreModule); world.transferOwnership(ROOT_NAMESPACE, msg.sender); emit WorldDeployed(worldAddress); diff --git a/packages/world/src/interfaces/IWorldErrors.sol b/packages/world/src/interfaces/IWorldErrors.sol index 3618ceb591..8804a161e9 100644 --- a/packages/world/src/interfaces/IWorldErrors.sol +++ b/packages/world/src/interfaces/IWorldErrors.sol @@ -2,6 +2,7 @@ pragma solidity >=0.8.0; interface IWorldErrors { + error WorldAlreadyInitialized(); error ResourceExists(string resource); error ResourceNotFound(string resource); error AccessDenied(string resource, address caller); @@ -9,7 +10,6 @@ interface IWorldErrors { error SystemExists(address system); error FunctionSelectorExists(bytes4 functionSelector); error FunctionSelectorNotFound(bytes4 functionSelector); - error ModuleAlreadyInstalled(string module); error DelegationNotFound(address delegator, address delegatee); error InsufficientBalance(uint256 balance, uint256 amount); error InterfaceNotSupported(address contractAddress, bytes4 interfaceId); diff --git a/packages/world/src/interfaces/IWorldKernel.sol b/packages/world/src/interfaces/IWorldKernel.sol index 8cf3511269..1dc9f602ad 100644 --- a/packages/world/src/interfaces/IWorldKernel.sol +++ b/packages/world/src/interfaces/IWorldKernel.sol @@ -40,4 +40,14 @@ interface IWorldCall { */ interface IWorldKernel is IWorldModuleInstallation, IWorldCall, IWorldErrors { event HelloWorld(); + + /** + * The immutable original deployer of the World. + */ + function creator() external view returns (address); + + /** + * Allows the creator of the World to initialize the World once. + */ + function initialize(IModule coreModule) external; } diff --git a/packages/world/src/modules/core/CoreModule.sol b/packages/world/src/modules/core/CoreModule.sol index 5c8c660875..aaf34ff19e 100644 --- a/packages/world/src/modules/core/CoreModule.sol +++ b/packages/world/src/modules/core/CoreModule.sol @@ -9,6 +9,7 @@ import { Module } from "../../Module.sol"; import { IBaseWorld } from "../../interfaces/IBaseWorld.sol"; import { IStoreEphemeral } from "@latticexyz/store/src/IStore.sol"; +import { StoreCore } from "@latticexyz/store/src/StoreCore.sol"; import { ResourceSelector } from "../../ResourceSelector.sol"; import { NamespaceOwner } from "../../tables/NamespaceOwner.sol"; @@ -63,6 +64,8 @@ contract CoreModule is Module { * Register core tables in the World */ function _registerCoreTables() internal { + StoreCore.registerCoreTables(); + NamespaceOwner.register(); Balances.register(); InstalledModules.register(); Delegations.register(); @@ -73,6 +76,7 @@ contract CoreModule is Module { SystemRegistry.register(); ResourceType.register(); + NamespaceOwner.set(ROOT_NAMESPACE, _msgSender()); ResourceAccess.set(ROOT_NAMESPACE, _msgSender(), true); ResourceType.set(ROOT_NAMESPACE, Resource.NAMESPACE); } diff --git a/packages/world/test/AccessControl.t.sol b/packages/world/test/AccessControl.t.sol index b9f571cd39..feddf64c67 100644 --- a/packages/world/test/AccessControl.t.sol +++ b/packages/world/test/AccessControl.t.sol @@ -3,7 +3,7 @@ pragma solidity >=0.8.0; import "forge-std/Test.sol"; import { GasReporter } from "@latticexyz/gas-report/src/GasReporter.sol"; -import { StoreReadWithStubs } from "@latticexyz/store/src/StoreReadWithStubs.sol"; +import { StoreMock } from "@latticexyz/store/test/StoreMock.sol"; import { IWorldErrors } from "../src/interfaces/IWorldErrors.sol"; import { World } from "../src/World.sol"; @@ -13,7 +13,7 @@ import { ResourceSelector } from "../src/ResourceSelector.sol"; import { ResourceAccess } from "../src/tables/ResourceAccess.sol"; import { NamespaceOwner } from "../src/tables/NamespaceOwner.sol"; -contract AccessControlTest is Test, GasReporter, StoreReadWithStubs { +contract AccessControlTest is Test, GasReporter, StoreMock { bytes16 constant namespace = "namespace"; bytes16 constant name = "name"; address constant presetCaller = address(0x0123); diff --git a/packages/world/test/KeysInTableModule.t.sol b/packages/world/test/KeysInTableModule.t.sol index 3bffe5dafd..e25f7b5427 100644 --- a/packages/world/test/KeysInTableModule.t.sol +++ b/packages/world/test/KeysInTableModule.t.sol @@ -57,7 +57,7 @@ contract KeysInTableModuleTest is Test, GasReporter { singletonKeySchema = SchemaLib.encode(new SchemaType[](0)); world = IBaseWorld(address(new World())); - world.installRootModule(new CoreModule(), new bytes(0)); + world.initialize(new CoreModule()); keyTuple1 = new bytes32[](1); keyTuple1[0] = key1; keyTuple2 = new bytes32[](1); diff --git a/packages/world/test/KeysWithValueModule.t.sol b/packages/world/test/KeysWithValueModule.t.sol index 708c4cd607..50307d969b 100644 --- a/packages/world/test/KeysWithValueModule.t.sol +++ b/packages/world/test/KeysWithValueModule.t.sol @@ -46,7 +46,7 @@ contract KeysWithValueModuleTest is Test, GasReporter { sourceTableSchema = SchemaEncodeHelper.encode(SchemaType.UINT256); sourceTableKeySchema = SchemaEncodeHelper.encode(SchemaType.BYTES32); world = IBaseWorld(address(new World())); - world.installRootModule(new CoreModule(), new bytes(0)); + world.initialize(new CoreModule()); keyTuple1 = new bytes32[](1); keyTuple1[0] = key1; keyTuple2 = new bytes32[](1); diff --git a/packages/world/test/StandardDelegationsModule.t.sol b/packages/world/test/StandardDelegationsModule.t.sol index 9ed9a716a3..72b83642d8 100644 --- a/packages/world/test/StandardDelegationsModule.t.sol +++ b/packages/world/test/StandardDelegationsModule.t.sol @@ -27,7 +27,7 @@ contract StandardDelegationsModuleTest is Test, GasReporter { function setUp() public { world = IBaseWorld(address(new World())); - world.installRootModule(new CoreModule(), new bytes(0)); + world.initialize(new CoreModule()); world.installRootModule(new StandardDelegationsModule(), new bytes(0)); // Register a new system diff --git a/packages/world/test/UniqueEntityModule.t.sol b/packages/world/test/UniqueEntityModule.t.sol index 7d8c18b4f7..22db658279 100644 --- a/packages/world/test/UniqueEntityModule.t.sol +++ b/packages/world/test/UniqueEntityModule.t.sol @@ -25,7 +25,7 @@ contract UniqueEntityModuleTest is Test, GasReporter { function setUp() public { world = IBaseWorld(address(new World())); - world.installRootModule(new CoreModule(), new bytes(0)); + world.initialize(new CoreModule()); } function testInstall() public { diff --git a/packages/world/test/Utils.t.sol b/packages/world/test/Utils.t.sol index 6a9aa2895d..de0acda0d0 100644 --- a/packages/world/test/Utils.t.sol +++ b/packages/world/test/Utils.t.sol @@ -25,7 +25,7 @@ contract UtilsTest is Test { function setUp() public { world = IBaseWorld(address(new World())); - world.installRootModule(new CoreModule(), new bytes(0)); + world.initialize(new CoreModule()); } function _registerAndGetNamespace(bytes16 namespace) internal returns (bytes16 returnedNamespace) { diff --git a/packages/world/test/World.t.sol b/packages/world/test/World.t.sol index 95510ceeb2..640782c591 100644 --- a/packages/world/test/World.t.sol +++ b/packages/world/test/World.t.sol @@ -157,7 +157,7 @@ contract WorldTest is Test, GasReporter { function setUp() public { world = IBaseWorld(address(new World())); - world.installRootModule(new CoreModule(), new bytes(0)); + world.initialize(new CoreModule()); key = "testKey"; keyTuple = new bytes32[](1); @@ -177,20 +177,49 @@ contract WorldTest is Test, GasReporter { ); } - function testConstructor() public { + function testConstructorAndInitialize() public { + CoreModule coreModule = new CoreModule(); + vm.expectEmit(true, true, true, true); emit HelloWorld(); - new World(); + IBaseWorld newWorld = IBaseWorld(address(new World())); + + // Expect the creator to be the original deployer + assertEq(newWorld.creator(), address(this)); + + // Expect calls to initialize to fail if the caller is not the creator + vm.prank(address(0x4242)); + vm.expectRevert( + abi.encodeWithSelector( + IWorldErrors.AccessDenied.selector, + ResourceSelector.from(ROOT_NAMESPACE).toString(), + address(0x4242) + ) + ); + newWorld.initialize(coreModule); + + // Expect the creator to be able to initialize the World + newWorld.initialize(coreModule); // Should have registered the table data table (fka schema table) - assertEq(Tables.getFieldLayout(world, TablesTableId), FieldLayout.unwrap(Tables.getFieldLayout())); - assertEq(Tables.getAbiEncodedKeyNames(world, TablesTableId), abi.encode(Tables.getKeyNames())); - assertEq(Tables.getAbiEncodedFieldNames(world, TablesTableId), abi.encode(Tables.getFieldNames())); + assertEq(Tables.getFieldLayout(newWorld, TablesTableId), FieldLayout.unwrap(Tables.getFieldLayout())); + assertEq(Tables.getAbiEncodedKeyNames(newWorld, TablesTableId), abi.encode(Tables.getKeyNames())); + assertEq(Tables.getAbiEncodedFieldNames(newWorld, TablesTableId), abi.encode(Tables.getFieldNames())); // Should have registered the namespace owner table - assertEq(Tables.getFieldLayout(world, NamespaceOwnerTableId), FieldLayout.unwrap(NamespaceOwner.getFieldLayout())); - assertEq(Tables.getAbiEncodedKeyNames(world, NamespaceOwnerTableId), abi.encode(NamespaceOwner.getKeyNames())); - assertEq(Tables.getAbiEncodedFieldNames(world, NamespaceOwnerTableId), abi.encode(NamespaceOwner.getFieldNames())); + assertEq( + Tables.getFieldLayout(newWorld, NamespaceOwnerTableId), + FieldLayout.unwrap(NamespaceOwner.getFieldLayout()) + ); + assertEq(Tables.getAbiEncodedKeyNames(newWorld, NamespaceOwnerTableId), abi.encode(NamespaceOwner.getKeyNames())); + assertEq( + Tables.getAbiEncodedFieldNames(newWorld, NamespaceOwnerTableId), + abi.encode(NamespaceOwner.getFieldNames()) + ); + + // Expect it to not be possible to initialize the World again + vm.expectRevert(abi.encodeWithSelector(IWorldErrors.WorldAlreadyInitialized.selector)); + newWorld.initialize(coreModule); } function testRegisterModuleRevertInterfaceNotSupported() public { diff --git a/packages/world/test/WorldBalance.t.sol b/packages/world/test/WorldBalance.t.sol index 50c46aeb88..f7225afde6 100644 --- a/packages/world/test/WorldBalance.t.sol +++ b/packages/world/test/WorldBalance.t.sol @@ -31,7 +31,7 @@ contract WorldBalanceTest is Test, GasReporter { function setUp() public { world = IBaseWorld(address(new World())); - world.installRootModule(new CoreModule(), new bytes(0)); + world.initialize(new CoreModule()); world.registerSystem(rootSystemId, rootSystem, true); world.registerSystem(nonRootSystemId, nonRootSystem, true); diff --git a/packages/world/test/WorldDynamicUpdate.t.sol b/packages/world/test/WorldDynamicUpdate.t.sol index 4d385eb850..fe84fdbc75 100644 --- a/packages/world/test/WorldDynamicUpdate.t.sol +++ b/packages/world/test/WorldDynamicUpdate.t.sol @@ -48,7 +48,7 @@ contract UpdateInFieldTest is Test, GasReporter { function setUp() public { world = IBaseWorld(address(new World())); - world.installRootModule(new CoreModule(), new bytes(0)); + world.initialize(new CoreModule()); key = "testKey"; keyTuple = new bytes32[](1); diff --git a/packages/world/test/query.t.sol b/packages/world/test/query.t.sol index 3a013610b9..4c4feb2176 100644 --- a/packages/world/test/query.t.sol +++ b/packages/world/test/query.t.sol @@ -51,7 +51,7 @@ contract QueryTest is Test, GasReporter { tableKeySchema = SchemaEncodeHelper.encode(SchemaType.BYTES32); tableValueSchema = SchemaEncodeHelper.encode(SchemaType.UINT256); world = IBaseWorld(address(new World())); - world.installRootModule(new CoreModule(), new bytes(0)); + world.initialize(new CoreModule()); key1[0] = "test1"; key2[0] = "test2";