From bdea7742bd36a9329569ef4b6daaf690d4c766e6 Mon Sep 17 00:00:00 2001 From: Dhvani Patel Date: Thu, 4 Jul 2024 11:31:56 +0100 Subject: [PATCH 1/8] feat: optional system hooks feature; function that gets triggered on unregister of a delegation; haskeys module --- packages/cli/src/deploy/getWorldContracts.ts | 17 +- packages/world-modules/mud.config.ts | 18 + packages/world-modules/src/index.sol | 1 + .../src/modules/haskeys/HasKeysHook.sol | 63 ++ .../src/modules/haskeys/HasKeysModule.sol | 87 ++ .../src/modules/haskeys/constants.sol | 7 + .../src/modules/haskeys/hasKey.sol | 28 + .../src/modules/haskeys/tables/HasKeys.sol | 290 ++++++ packages/world/mud.config.ts | 15 + .../world/src/ICustomUnregisterDelegation.sol | 8 + packages/world/src/IOptionalSystemHook.sol | 44 + packages/world/src/IWorldErrors.sol | 14 + packages/world/src/SystemCall.sol | 35 + packages/world/src/codegen/index.sol | 1 + .../src/codegen/interfaces/IBaseWorld.sol | 4 + .../IExtendedRegistrationSystem.sol | 11 + .../IExtendedWorldRegistrationSystem.sol | 27 + .../codegen/tables/OptionalSystemHooks.sol | 853 ++++++++++++++++++ .../codegen/tables/UserDelegationControl.sol | 89 ++ .../init/ExtendedRegistrationSystem.sol | 16 + .../world/src/modules/init/InitModule.sol | 19 +- packages/world/src/modules/init/constants.sol | 8 + .../src/modules/init/functionSignatures.sol | 11 + .../ExtendedWorldRegistrationSystem.sol | 137 +++ .../WorldRegistrationSystem.sol | 11 + packages/world/test/createInitModule.sol | 4 +- 26 files changed, 1813 insertions(+), 5 deletions(-) create mode 100644 packages/world-modules/src/modules/haskeys/HasKeysHook.sol create mode 100644 packages/world-modules/src/modules/haskeys/HasKeysModule.sol create mode 100644 packages/world-modules/src/modules/haskeys/constants.sol create mode 100644 packages/world-modules/src/modules/haskeys/hasKey.sol create mode 100644 packages/world-modules/src/modules/haskeys/tables/HasKeys.sol create mode 100644 packages/world/src/ICustomUnregisterDelegation.sol create mode 100644 packages/world/src/IOptionalSystemHook.sol create mode 100644 packages/world/src/codegen/interfaces/IExtendedRegistrationSystem.sol create mode 100644 packages/world/src/codegen/interfaces/IExtendedWorldRegistrationSystem.sol create mode 100644 packages/world/src/codegen/tables/OptionalSystemHooks.sol create mode 100644 packages/world/src/modules/init/ExtendedRegistrationSystem.sol create mode 100644 packages/world/src/modules/init/implementations/ExtendedWorldRegistrationSystem.sol diff --git a/packages/cli/src/deploy/getWorldContracts.ts b/packages/cli/src/deploy/getWorldContracts.ts index cb39d5400e..6337fcd68d 100644 --- a/packages/cli/src/deploy/getWorldContracts.ts +++ b/packages/cli/src/deploy/getWorldContracts.ts @@ -2,6 +2,7 @@ import accessManagementSystemBuild from "@latticexyz/world/out/AccessManagementS import balanceTransferSystemBuild from "@latticexyz/world/out/BalanceTransferSystem.sol/BalanceTransferSystem.json" assert { type: "json" }; import batchCallSystemBuild from "@latticexyz/world/out/BatchCallSystem.sol/BatchCallSystem.json" assert { type: "json" }; import registrationSystemBuild from "@latticexyz/world/out/RegistrationSystem.sol/RegistrationSystem.json" assert { type: "json" }; +import extendedRegistrationSystemBuild from "@latticexyz/world/out/ExtendedRegistrationSystem.sol/ExtendedRegistrationSystem.json" assert { type: "json" }; import initModuleBuild from "@latticexyz/world/out/InitModule.sol/InitModule.json" assert { type: "json" }; import initModuleAbi from "@latticexyz/world/out/InitModule.sol/InitModule.abi.json" assert { type: "json" }; import { Hex, getCreate2Address, encodeDeployData, size } from "viem"; @@ -36,11 +37,19 @@ export function getWorldContracts(deployerAddress: Hex) { salt, }); + const extendedRegistrationDeployedBytecodeSize = size(extendedRegistrationSystemBuild.deployedBytecode.object as Hex); + const extendedRegistrationBytecode = extendedRegistrationSystemBuild.bytecode.object as Hex; + const extendedRegistration = getCreate2Address({ + from: deployerAddress, + bytecode: extendedRegistrationBytecode, + salt, + }); + const initModuleDeployedBytecodeSize = size(initModuleBuild.deployedBytecode.object as Hex); const initModuleBytecode = encodeDeployData({ bytecode: initModuleBuild.bytecode.object as Hex, abi: initModuleAbi, - args: [accessManagementSystem, balanceTransferSystem, batchCallSystem, registration], + args: [accessManagementSystem, balanceTransferSystem, batchCallSystem, registration, extendedRegistration], }); const initModule = getCreate2Address({ from: deployerAddress, bytecode: initModuleBytecode, salt }); @@ -69,6 +78,12 @@ export function getWorldContracts(deployerAddress: Hex) { label: "core registration system", address: registration, }, + ExtendedRegistrationSystem: { + bytecode: extendedRegistrationBytecode, + deployedBytecodeSize: extendedRegistrationDeployedBytecodeSize, + label: "extended core registration system", + address: extendedRegistration, + }, InitModule: { bytecode: initModuleBytecode, deployedBytecodeSize: initModuleDeployedBytecodeSize, diff --git a/packages/world-modules/mud.config.ts b/packages/world-modules/mud.config.ts index c9813db299..c81247c76e 100644 --- a/packages/world-modules/mud.config.ts +++ b/packages/world-modules/mud.config.ts @@ -61,6 +61,24 @@ export default defineWorld({ storeArgument: true, }, }, + /************************************************************************ + * + * HAS KEYS MODULE + * + ************************************************************************/ + HasKeys: { + schema: { + sourceTable: "ResourceId", + keysHash: "bytes32", + has: "bool", + }, + key: ["sourceTable", "keysHash"], + codegen: { + outputDirectory: "modules/haskeys/tables", + dataStruct: false, + storeArgument: true, + }, + }, /************************************************************************ * * UNIQUE ENTITY MODULE diff --git a/packages/world-modules/src/index.sol b/packages/world-modules/src/index.sol index 5fa265ec1f..fc57fd688c 100644 --- a/packages/world-modules/src/index.sol +++ b/packages/world-modules/src/index.sol @@ -6,6 +6,7 @@ pragma solidity >=0.8.24; import { KeysWithValue } from "./modules/keyswithvalue/tables/KeysWithValue.sol"; import { KeysInTable, KeysInTableData } from "./modules/keysintable/tables/KeysInTable.sol"; import { UsedKeysIndex } from "./modules/keysintable/tables/UsedKeysIndex.sol"; +import { HasKeys } from "./modules/haskeys/tables/HasKeys.sol"; import { UniqueEntity } from "./modules/uniqueentity/tables/UniqueEntity.sol"; import { CallboundDelegations } from "./modules/std-delegations/tables/CallboundDelegations.sol"; import { SystemboundDelegations } from "./modules/std-delegations/tables/SystemboundDelegations.sol"; diff --git a/packages/world-modules/src/modules/haskeys/HasKeysHook.sol b/packages/world-modules/src/modules/haskeys/HasKeysHook.sol new file mode 100644 index 0000000000..8e82dbe80a --- /dev/null +++ b/packages/world-modules/src/modules/haskeys/HasKeysHook.sol @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.24; + +import { EncodedLengths } from "@latticexyz/store/src/EncodedLengths.sol"; +import { FieldLayout } from "@latticexyz/store/src/FieldLayout.sol"; +import { StoreHook } from "@latticexyz/store/src/StoreHook.sol"; +import { ResourceId } from "@latticexyz/store/src/ResourceId.sol"; + +import { HasKeys } from "./tables/HasKeys.sol"; + +/** + * Note: if a table with composite keys is used, only the first five keys of the tuple are indexed + */ +contract HasKeysHook is StoreHook { + function handleSet(ResourceId tableId, bytes32[] memory keyTuple) internal { + bytes32 keysHash = keccak256(abi.encode(keyTuple)); + + // If the key has not yet been set in the table... + if (!HasKeys.get(tableId, keysHash)) { + HasKeys.set(tableId, keysHash, true); + } + } + + function onBeforeSetRecord( + ResourceId tableId, + bytes32[] memory keyTuple, + bytes memory, + EncodedLengths, + bytes memory, + FieldLayout + ) public override { + handleSet(tableId, keyTuple); + } + + function onAfterSpliceStaticData( + ResourceId tableId, + bytes32[] memory keyTuple, + uint48, + bytes memory + ) public override { + handleSet(tableId, keyTuple); + } + + function onAfterSpliceDynamicData( + ResourceId tableId, + bytes32[] memory keyTuple, + uint8, + uint40, + uint40, + EncodedLengths, + bytes memory + ) public override { + handleSet(tableId, keyTuple); + } + + function onBeforeDeleteRecord(ResourceId tableId, bytes32[] memory keyTuple, FieldLayout) public override { + bytes32 keysHash = keccak256(abi.encode(keyTuple)); + + if (HasKeys.get(tableId, keysHash)) { + HasKeys.deleteRecord(tableId, keysHash); + } + } +} diff --git a/packages/world-modules/src/modules/haskeys/HasKeysModule.sol b/packages/world-modules/src/modules/haskeys/HasKeysModule.sol new file mode 100644 index 0000000000..0a02b9b787 --- /dev/null +++ b/packages/world-modules/src/modules/haskeys/HasKeysModule.sol @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.24; + +import { BEFORE_SET_RECORD, AFTER_SPLICE_STATIC_DATA, AFTER_SPLICE_DYNAMIC_DATA, BEFORE_DELETE_RECORD } from "@latticexyz/store/src/storeHookTypes.sol"; +import { ResourceIds } from "@latticexyz/store/src/codegen/tables/ResourceIds.sol"; + +import { Module } from "@latticexyz/world/src/Module.sol"; + +import { IBaseWorld } from "@latticexyz/world/src/codegen/interfaces/IBaseWorld.sol"; +import { InstalledModules } from "@latticexyz/world/src/codegen/index.sol"; + +import { ResourceId, WorldResourceIdInstance } from "@latticexyz/world/src/WorldResourceId.sol"; +import { revertWithBytes } from "@latticexyz/world/src/revertWithBytes.sol"; + +import { HasKeysHook } from "./HasKeysHook.sol"; +import { HasKeys } from "./tables/HasKeys.sol"; + +/** + * This module deploys a hook that is called when a value is set in the `sourceTableId` + * provided in the install methods arguments. The hook keeps track of the keys that are in a given table. + * This mapping is stored in a global table that is registered when the module is first installed. + * + * Note: this module currently only supports `installRoot` (via `World.installRootModule`). + * TODO: add support for `install` (via `World.installModule`) by using `callFrom` with the `msgSender()` + */ +contract HasKeysModule is Module { + using WorldResourceIdInstance for ResourceId; + + // The HasKeysHook is deployed once and infers the target table id + // from the source table id (passed as argument to the hook methods) + HasKeysHook private immutable hook = new HasKeysHook(); + + function installRoot(bytes memory encodedArgs) public override { + // Naive check to ensure this is only installed once + // TODO: only revert if there's nothing to do + requireNotInstalled(__self, encodedArgs); + + // Extract source table id from args + ResourceId sourceTableId = ResourceId.wrap(abi.decode(encodedArgs, (bytes32))); + + IBaseWorld world = IBaseWorld(_world()); + + // Initialize variable to reuse in low level calls + bool success; + bytes memory returnData; + + if (!ResourceIds._getExists(HasKeys._tableId)) { + // Register the tables + (success, returnData) = address(world).delegatecall( + abi.encodeCall( + world.registerTable, + ( + HasKeys._tableId, + HasKeys._fieldLayout, + HasKeys._keySchema, + HasKeys._valueSchema, + HasKeys.getKeyNames(), + HasKeys.getFieldNames() + ) + ) + ); + if (!success) revertWithBytes(returnData); + + // Grant the hook access to the tables + (success, returnData) = address(world).delegatecall( + abi.encodeCall(world.grantAccess, (HasKeys._tableId, address(hook))) + ); + if (!success) revertWithBytes(returnData); + } + + // Register a hook that is called when a value is set in the source table + (success, returnData) = address(world).delegatecall( + abi.encodeCall( + world.registerStoreHook, + ( + sourceTableId, + hook, + BEFORE_SET_RECORD | AFTER_SPLICE_STATIC_DATA | AFTER_SPLICE_DYNAMIC_DATA | BEFORE_DELETE_RECORD + ) + ) + ); + } + + function install(bytes memory) public pure { + revert Module_NonRootInstallNotSupported(); + } +} diff --git a/packages/world-modules/src/modules/haskeys/constants.sol b/packages/world-modules/src/modules/haskeys/constants.sol new file mode 100644 index 0000000000..8ec3d34edf --- /dev/null +++ b/packages/world-modules/src/modules/haskeys/constants.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.24; + +// Limiting the module namespace to 8 bytes so the last 8 bytes +// can be used for an identifier of the source table namespace to avoid +// collisions between tables with the same name in different namespaces +bytes8 constant MODULE_NAMESPACE = "haskeys"; diff --git a/packages/world-modules/src/modules/haskeys/hasKey.sol b/packages/world-modules/src/modules/haskeys/hasKey.sol new file mode 100644 index 0000000000..3e9055440f --- /dev/null +++ b/packages/world-modules/src/modules/haskeys/hasKey.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.24; + +import { IStore } from "@latticexyz/store/src/IStore.sol"; +import { ResourceId } from "@latticexyz/store/src/ResourceId.sol"; + +import { HasKeys } from "./tables/HasKeys.sol"; + +/** + * Get whether the keyTuple is in the given table. + * + * Note: this util can only be called within the context of a Store (e.g. from a System or Module). + * For usage outside of a Store, use the overload that takes an explicit store argument. + */ +function hasKey(ResourceId tableId, bytes32[] memory keyTuple) view returns (bool) { + bytes32 keysHash = keccak256(abi.encode(keyTuple)); + + return HasKeys.get(tableId, keysHash); +} + +/** + * Get whether the keyTuple is in the given table for the given store. + */ +function hasKey(IStore store, ResourceId tableId, bytes32[] memory keyTuple) view returns (bool) { + bytes32 keysHash = keccak256(abi.encode(keyTuple)); + + return HasKeys.get(store, tableId, keysHash); +} diff --git a/packages/world-modules/src/modules/haskeys/tables/HasKeys.sol b/packages/world-modules/src/modules/haskeys/tables/HasKeys.sol new file mode 100644 index 0000000000..084e1ac99f --- /dev/null +++ b/packages/world-modules/src/modules/haskeys/tables/HasKeys.sol @@ -0,0 +1,290 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.24; + +/* Autogenerated file. Do not edit manually. */ + +// Import store internals +import { IStore } from "@latticexyz/store/src/IStore.sol"; +import { StoreSwitch } from "@latticexyz/store/src/StoreSwitch.sol"; +import { StoreCore } from "@latticexyz/store/src/StoreCore.sol"; +import { Bytes } from "@latticexyz/store/src/Bytes.sol"; +import { Memory } from "@latticexyz/store/src/Memory.sol"; +import { SliceLib } from "@latticexyz/store/src/Slice.sol"; +import { EncodeArray } from "@latticexyz/store/src/tightcoder/EncodeArray.sol"; +import { FieldLayout } from "@latticexyz/store/src/FieldLayout.sol"; +import { Schema } from "@latticexyz/store/src/Schema.sol"; +import { EncodedLengths, EncodedLengthsLib } from "@latticexyz/store/src/EncodedLengths.sol"; +import { ResourceId } from "@latticexyz/store/src/ResourceId.sol"; + +// Import user types +import { ResourceId } from "@latticexyz/store/src/ResourceId.sol"; + +library HasKeys { + // Hex below is the result of `WorldResourceIdLib.encode({ namespace: "", name: "HasKeys", typeId: RESOURCE_TABLE });` + ResourceId constant _tableId = ResourceId.wrap(0x746200000000000000000000000000004861734b657973000000000000000000); + + FieldLayout constant _fieldLayout = + FieldLayout.wrap(0x0001010001000000000000000000000000000000000000000000000000000000); + + // Hex-encoded key schema of (bytes32, bytes32) + Schema constant _keySchema = Schema.wrap(0x004002005f5f0000000000000000000000000000000000000000000000000000); + // Hex-encoded value schema of (bool) + Schema constant _valueSchema = Schema.wrap(0x0001010060000000000000000000000000000000000000000000000000000000); + + /** + * @notice Get the table's key field names. + * @return keyNames An array of strings with the names of key fields. + */ + function getKeyNames() internal pure returns (string[] memory keyNames) { + keyNames = new string[](2); + keyNames[0] = "sourceTable"; + keyNames[1] = "keysHash"; + } + + /** + * @notice Get the table's value field names. + * @return fieldNames An array of strings with the names of value fields. + */ + function getFieldNames() internal pure returns (string[] memory fieldNames) { + fieldNames = new string[](1); + fieldNames[0] = "has"; + } + + /** + * @notice Register the table with its config. + */ + function register() internal { + StoreSwitch.registerTable(_tableId, _fieldLayout, _keySchema, _valueSchema, getKeyNames(), getFieldNames()); + } + + /** + * @notice Register the table with its config. + */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, _keySchema, _valueSchema, getKeyNames(), getFieldNames()); + } + + /** + * @notice Register the table with its config (using the specified store). + */ + function register(IStore _store) internal { + _store.registerTable(_tableId, _fieldLayout, _keySchema, _valueSchema, getKeyNames(), getFieldNames()); + } + + /** + * @notice Get has. + */ + function getHas(ResourceId sourceTable, bytes32 keysHash) internal view returns (bool has) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = ResourceId.unwrap(sourceTable); + _keyTuple[1] = keysHash; + + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (_toBool(uint8(bytes1(_blob)))); + } + + /** + * @notice Get has. + */ + function _getHas(ResourceId sourceTable, bytes32 keysHash) internal view returns (bool has) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = ResourceId.unwrap(sourceTable); + _keyTuple[1] = keysHash; + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (_toBool(uint8(bytes1(_blob)))); + } + + /** + * @notice Get has (using the specified store). + */ + function getHas(IStore _store, ResourceId sourceTable, bytes32 keysHash) internal view returns (bool has) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = ResourceId.unwrap(sourceTable); + _keyTuple[1] = keysHash; + + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (_toBool(uint8(bytes1(_blob)))); + } + + /** + * @notice Get has. + */ + function get(ResourceId sourceTable, bytes32 keysHash) internal view returns (bool has) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = ResourceId.unwrap(sourceTable); + _keyTuple[1] = keysHash; + + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (_toBool(uint8(bytes1(_blob)))); + } + + /** + * @notice Get has. + */ + function _get(ResourceId sourceTable, bytes32 keysHash) internal view returns (bool has) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = ResourceId.unwrap(sourceTable); + _keyTuple[1] = keysHash; + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (_toBool(uint8(bytes1(_blob)))); + } + + /** + * @notice Get has (using the specified store). + */ + function get(IStore _store, ResourceId sourceTable, bytes32 keysHash) internal view returns (bool has) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = ResourceId.unwrap(sourceTable); + _keyTuple[1] = keysHash; + + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (_toBool(uint8(bytes1(_blob)))); + } + + /** + * @notice Set has. + */ + function setHas(ResourceId sourceTable, bytes32 keysHash, bool has) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = ResourceId.unwrap(sourceTable); + _keyTuple[1] = keysHash; + + StoreSwitch.setStaticField(_tableId, _keyTuple, 0, abi.encodePacked((has)), _fieldLayout); + } + + /** + * @notice Set has. + */ + function _setHas(ResourceId sourceTable, bytes32 keysHash, bool has) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = ResourceId.unwrap(sourceTable); + _keyTuple[1] = keysHash; + + StoreCore.setStaticField(_tableId, _keyTuple, 0, abi.encodePacked((has)), _fieldLayout); + } + + /** + * @notice Set has (using the specified store). + */ + function setHas(IStore _store, ResourceId sourceTable, bytes32 keysHash, bool has) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = ResourceId.unwrap(sourceTable); + _keyTuple[1] = keysHash; + + _store.setStaticField(_tableId, _keyTuple, 0, abi.encodePacked((has)), _fieldLayout); + } + + /** + * @notice Set has. + */ + function set(ResourceId sourceTable, bytes32 keysHash, bool has) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = ResourceId.unwrap(sourceTable); + _keyTuple[1] = keysHash; + + StoreSwitch.setStaticField(_tableId, _keyTuple, 0, abi.encodePacked((has)), _fieldLayout); + } + + /** + * @notice Set has. + */ + function _set(ResourceId sourceTable, bytes32 keysHash, bool has) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = ResourceId.unwrap(sourceTable); + _keyTuple[1] = keysHash; + + StoreCore.setStaticField(_tableId, _keyTuple, 0, abi.encodePacked((has)), _fieldLayout); + } + + /** + * @notice Set has (using the specified store). + */ + function set(IStore _store, ResourceId sourceTable, bytes32 keysHash, bool has) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = ResourceId.unwrap(sourceTable); + _keyTuple[1] = keysHash; + + _store.setStaticField(_tableId, _keyTuple, 0, abi.encodePacked((has)), _fieldLayout); + } + + /** + * @notice Delete all data for given keys. + */ + function deleteRecord(ResourceId sourceTable, bytes32 keysHash) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = ResourceId.unwrap(sourceTable); + _keyTuple[1] = keysHash; + + StoreSwitch.deleteRecord(_tableId, _keyTuple); + } + + /** + * @notice Delete all data for given keys. + */ + function _deleteRecord(ResourceId sourceTable, bytes32 keysHash) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = ResourceId.unwrap(sourceTable); + _keyTuple[1] = keysHash; + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /** + * @notice Delete all data for given keys (using the specified store). + */ + function deleteRecord(IStore _store, ResourceId sourceTable, bytes32 keysHash) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = ResourceId.unwrap(sourceTable); + _keyTuple[1] = keysHash; + + _store.deleteRecord(_tableId, _keyTuple); + } + + /** + * @notice Tightly pack static (fixed length) data using this table's schema. + * @return The static data, encoded into a sequence of bytes. + */ + function encodeStatic(bool has) internal pure returns (bytes memory) { + return abi.encodePacked(has); + } + + /** + * @notice Encode all of a record's fields. + * @return The static (fixed length) data, encoded into a sequence of bytes. + * @return The lengths of the dynamic fields (packed into a single bytes32 value). + * @return The dynamic (variable length) data, encoded into a sequence of bytes. + */ + function encode(bool has) internal pure returns (bytes memory, EncodedLengths, bytes memory) { + bytes memory _staticData = encodeStatic(has); + + EncodedLengths _encodedLengths; + bytes memory _dynamicData; + + return (_staticData, _encodedLengths, _dynamicData); + } + + /** + * @notice Encode keys as a bytes32 array using this table's field layout. + */ + function encodeKeyTuple(ResourceId sourceTable, bytes32 keysHash) internal pure returns (bytes32[] memory) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = ResourceId.unwrap(sourceTable); + _keyTuple[1] = keysHash; + + return _keyTuple; + } +} + +/** + * @notice Cast a value to a bool. + * @dev Boolean values are encoded as uint8 (1 = true, 0 = false), but Solidity doesn't allow casting between uint8 and bool. + * @param value The uint8 value to convert. + * @return result The boolean value. + */ +function _toBool(uint8 value) pure returns (bool result) { + assembly { + result := value + } +} diff --git a/packages/world/mud.config.ts b/packages/world/mud.config.ts index 6ce75e044f..c1ef79a9ee 100644 --- a/packages/world/mud.config.ts +++ b/packages/world/mud.config.ts @@ -41,6 +41,9 @@ export default defineWorld({ delegationControlId: "ResourceId", }, key: ["delegator", "delegatee"], + codegen: { + storeArgument: true, + }, }, NamespaceDelegationControl: { schema: { @@ -81,6 +84,18 @@ export default defineWorld({ }, key: ["systemId"], }, + OptionalSystemHooks: { + schema: { + player: "address", + systemId: "ResourceId", + callDataHash: "bytes32", + hooks: "bytes21[]", + }, + key: ["player", "systemId", "callDataHash"], + codegen: { + storeArgument: true, + }, + }, FunctionSelectors: { schema: { worldFunctionSelector: "bytes4", diff --git a/packages/world/src/ICustomUnregisterDelegation.sol b/packages/world/src/ICustomUnregisterDelegation.sol new file mode 100644 index 0000000000..1d4da9c67e --- /dev/null +++ b/packages/world/src/ICustomUnregisterDelegation.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.24; + +import { IERC165 } from "./IERC165.sol"; + +interface ICustomUnregisterDelegation is IERC165 { + function canUnregister(address delegator) external returns (bool); +} diff --git a/packages/world/src/IOptionalSystemHook.sol b/packages/world/src/IOptionalSystemHook.sol new file mode 100644 index 0000000000..cf5682d35c --- /dev/null +++ b/packages/world/src/IOptionalSystemHook.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.24; + +import { ResourceId } from "@latticexyz/store/src/ResourceId.sol"; +import { ISystemHook } from "./ISystemHook.sol"; +import { Hook } from "@latticexyz/store/src/Hook.sol"; + +/** + * @title IOptionalSystemHook + * @dev Interface defining optional hooks for external functionality. + * Provides pre and post hooks that can be triggered before and after a system call respectively. + * This interface adheres to the ERC-165 standard for determining interface support. + */ +interface IOptionalSystemHook is ISystemHook { + /** + * @notice Executes when a system hook is registered by the user. + * @dev Provides the ability to add custom logic or checks when a system hook is registered. + * @param msgSender The original sender of the system call. + * @param systemId The ID of the system + * @param enabledHooksBitmap Bitmap indicating which hooks are enabled + * @param callDataHash The hash of the call data for the system hook + */ + function onRegisterHook( + address msgSender, + ResourceId systemId, + uint8 enabledHooksBitmap, + bytes32 callDataHash + ) external; + + /** + * @notice Executes when a system hook is unregistered by the user. + * @dev Provides the ability to add custom logic or checks when a system hook is unregistered. + * @param msgSender The original sender of the system call. + * @param systemId The ID of the system + * @param enabledHooksBitmap Bitmap indicating which hooks are enabled + * @param callDataHash The hash of the call data for the system hook + */ + function onUnregisterHook( + address msgSender, + ResourceId systemId, + uint8 enabledHooksBitmap, + bytes32 callDataHash + ) external; +} diff --git a/packages/world/src/IWorldErrors.sol b/packages/world/src/IWorldErrors.sol index e702f86695..5627154562 100644 --- a/packages/world/src/IWorldErrors.sol +++ b/packages/world/src/IWorldErrors.sol @@ -80,6 +80,12 @@ interface IWorldErrors { */ error World_UnlimitedDelegationNotAllowed(); + /** + * @notice Raised when unregister delegation is called but a custom unregister delegation blocks it + * e.g. if the delegation agreement wanted to enforce some conditions before allowing the delegation to be removed + */ + error World_CustomUnregisterDelegationNotAllowed(); + /** * @notice Raised when there's an insufficient balance for a particular operation. * @param balance The current balance. @@ -107,4 +113,12 @@ interface IWorldErrors { * @param functionSelector The function selector of the disallowed callback. */ error World_CallbackNotAllowed(bytes4 functionSelector); + + /** + * @notice Raised when trying to register an optional system hook that is already registered. + * @param systemId The ID of the system. + * @param hookAddress The address of the hook. + * @param callDataHash The hash of the call data. + */ + error World_OptionalHookAlreadyRegistered(ResourceId systemId, address hookAddress, bytes32 callDataHash); } diff --git a/packages/world/src/SystemCall.sol b/packages/world/src/SystemCall.sol index a60ffe579e..bd716caa0b 100644 --- a/packages/world/src/SystemCall.sol +++ b/packages/world/src/SystemCall.sol @@ -12,9 +12,11 @@ import { BEFORE_CALL_SYSTEM, AFTER_CALL_SYSTEM } from "./systemHookTypes.sol"; import { IWorldErrors } from "./IWorldErrors.sol"; import { ISystemHook } from "./ISystemHook.sol"; +import { IOptionalSystemHook } from "./IOptionalSystemHook.sol"; import { Systems } from "./codegen/tables/Systems.sol"; import { SystemHooks } from "./codegen/tables/SystemHooks.sol"; +import { OptionalSystemHooks } from "./codegen/tables/OptionalSystemHooks.sol"; import { Balances } from "./codegen/tables/Balances.sol"; /** @@ -93,6 +95,10 @@ library SystemCall { // Get system hooks bytes21[] memory hooks = SystemHooks._get(systemId); + // Get optional hooks specified by the caller + bytes21[] memory optionalSystemHooks = OptionalSystemHooks._get(caller, systemId, bytes32(0)); + bytes21[] memory optionalSystemHooksWithCallData = OptionalSystemHooks._get(caller, systemId, keccak256(callData)); + // Call onBeforeCallSystem hooks (before calling the system) for (uint256 i; i < hooks.length; i++) { Hook hook = Hook.wrap(hooks[i]); @@ -101,6 +107,21 @@ library SystemCall { } } + // Call optional onBeforeCallSystem hooks (before calling the system) + for (uint256 i; i < optionalSystemHooks.length; i++) { + Hook hook = Hook.wrap(optionalSystemHooks[i]); + if (hook.isEnabled(BEFORE_CALL_SYSTEM)) { + IOptionalSystemHook(hook.getAddress()).onBeforeCallSystem(caller, systemId, callData); + } + } + + for (uint256 i; i < optionalSystemHooksWithCallData.length; i++) { + Hook hook = Hook.wrap(optionalSystemHooksWithCallData[i]); + if (hook.isEnabled(BEFORE_CALL_SYSTEM)) { + IOptionalSystemHook(hook.getAddress()).onBeforeCallSystem(caller, systemId, callData); + } + } + // Call the system and forward any return data (success, data) = call({ caller: caller, value: value, systemId: systemId, callData: callData }); @@ -111,6 +132,20 @@ library SystemCall { ISystemHook(hook.getAddress()).onAfterCallSystem(caller, systemId, callData); } } + + // Call optional onAfterCallSystem hooks (after calling the system) + for (uint256 i; i < optionalSystemHooks.length; i++) { + Hook hook = Hook.wrap(optionalSystemHooks[i]); + if (hook.isEnabled(AFTER_CALL_SYSTEM)) { + IOptionalSystemHook(hook.getAddress()).onAfterCallSystem(caller, systemId, callData); + } + } + for (uint256 i; i < optionalSystemHooksWithCallData.length; i++) { + Hook hook = Hook.wrap(optionalSystemHooksWithCallData[i]); + if (hook.isEnabled(AFTER_CALL_SYSTEM)) { + IOptionalSystemHook(hook.getAddress()).onAfterCallSystem(caller, systemId, callData); + } + } } /** diff --git a/packages/world/src/codegen/index.sol b/packages/world/src/codegen/index.sol index b00d58a490..1b9586c5b7 100644 --- a/packages/world/src/codegen/index.sol +++ b/packages/world/src/codegen/index.sol @@ -12,6 +12,7 @@ import { Balances } from "./tables/Balances.sol"; import { Systems } from "./tables/Systems.sol"; import { SystemRegistry } from "./tables/SystemRegistry.sol"; import { SystemHooks } from "./tables/SystemHooks.sol"; +import { OptionalSystemHooks } from "./tables/OptionalSystemHooks.sol"; import { FunctionSelectors } from "./tables/FunctionSelectors.sol"; import { FunctionSignatures } from "./tables/FunctionSignatures.sol"; import { InitModuleAddress } from "./tables/InitModuleAddress.sol"; diff --git a/packages/world/src/codegen/interfaces/IBaseWorld.sol b/packages/world/src/codegen/interfaces/IBaseWorld.sol index 9d2ba9e356..55d5dffbce 100644 --- a/packages/world/src/codegen/interfaces/IBaseWorld.sol +++ b/packages/world/src/codegen/interfaces/IBaseWorld.sol @@ -6,10 +6,12 @@ pragma solidity >=0.8.24; import { IStore } from "@latticexyz/store/src/IStore.sol"; import { IWorldKernel } from "../../IWorldKernel.sol"; +import { IExtendedRegistrationSystem } from "./IExtendedRegistrationSystem.sol"; import { IRegistrationSystem } from "./IRegistrationSystem.sol"; import { IAccessManagementSystem } from "./IAccessManagementSystem.sol"; import { IBalanceTransferSystem } from "./IBalanceTransferSystem.sol"; import { IBatchCallSystem } from "./IBatchCallSystem.sol"; +import { IExtendedWorldRegistrationSystem } from "./IExtendedWorldRegistrationSystem.sol"; import { IModuleInstallationSystem } from "./IModuleInstallationSystem.sol"; import { IWorldRegistrationSystem } from "./IWorldRegistrationSystem.sol"; @@ -23,10 +25,12 @@ import { IWorldRegistrationSystem } from "./IWorldRegistrationSystem.sol"; interface IBaseWorld is IStore, IWorldKernel, + IExtendedRegistrationSystem, IRegistrationSystem, IAccessManagementSystem, IBalanceTransferSystem, IBatchCallSystem, + IExtendedWorldRegistrationSystem, IModuleInstallationSystem, IWorldRegistrationSystem {} diff --git a/packages/world/src/codegen/interfaces/IExtendedRegistrationSystem.sol b/packages/world/src/codegen/interfaces/IExtendedRegistrationSystem.sol new file mode 100644 index 0000000000..69ddf03873 --- /dev/null +++ b/packages/world/src/codegen/interfaces/IExtendedRegistrationSystem.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.24; + +/* Autogenerated file. Do not edit manually. */ + +/** + * @title IExtendedRegistrationSystem + * @author MUD (https://mud.dev) by Lattice (https://lattice.xyz) + * @dev This interface is automatically generated from the corresponding system contract. Do not edit manually. + */ +interface IExtendedRegistrationSystem {} diff --git a/packages/world/src/codegen/interfaces/IExtendedWorldRegistrationSystem.sol b/packages/world/src/codegen/interfaces/IExtendedWorldRegistrationSystem.sol new file mode 100644 index 0000000000..e53c51643a --- /dev/null +++ b/packages/world/src/codegen/interfaces/IExtendedWorldRegistrationSystem.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.24; + +/* Autogenerated file. Do not edit manually. */ + +import { ResourceId } from "@latticexyz/store/src/ResourceId.sol"; +import { IOptionalSystemHook } from "./../../IOptionalSystemHook.sol"; + +/** + * @title IExtendedWorldRegistrationSystem + * @author MUD (https://mud.dev) by Lattice (https://lattice.xyz) + * @dev This interface is automatically generated from the corresponding system contract. Do not edit manually. + */ +interface IExtendedWorldRegistrationSystem { + function registerOptionalSystemHook( + ResourceId systemId, + IOptionalSystemHook hookAddress, + uint8 enabledHooksBitmap, + bytes32 callDataHash + ) external; + + function unregisterOptionalSystemHook( + ResourceId systemId, + IOptionalSystemHook hookAddress, + bytes32 callDataHash + ) external; +} diff --git a/packages/world/src/codegen/tables/OptionalSystemHooks.sol b/packages/world/src/codegen/tables/OptionalSystemHooks.sol new file mode 100644 index 0000000000..9ae3230e1d --- /dev/null +++ b/packages/world/src/codegen/tables/OptionalSystemHooks.sol @@ -0,0 +1,853 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.24; + +/* Autogenerated file. Do not edit manually. */ + +// Import store internals +import { IStore } from "@latticexyz/store/src/IStore.sol"; +import { StoreSwitch } from "@latticexyz/store/src/StoreSwitch.sol"; +import { StoreCore } from "@latticexyz/store/src/StoreCore.sol"; +import { Bytes } from "@latticexyz/store/src/Bytes.sol"; +import { Memory } from "@latticexyz/store/src/Memory.sol"; +import { SliceLib } from "@latticexyz/store/src/Slice.sol"; +import { EncodeArray } from "@latticexyz/store/src/tightcoder/EncodeArray.sol"; +import { FieldLayout } from "@latticexyz/store/src/FieldLayout.sol"; +import { Schema } from "@latticexyz/store/src/Schema.sol"; +import { EncodedLengths, EncodedLengthsLib } from "@latticexyz/store/src/EncodedLengths.sol"; +import { ResourceId } from "@latticexyz/store/src/ResourceId.sol"; + +// Import user types +import { ResourceId } from "@latticexyz/store/src/ResourceId.sol"; + +library OptionalSystemHooks { + // Hex below is the result of `WorldResourceIdLib.encode({ namespace: "world", name: "OptionalSystemHo", typeId: RESOURCE_TABLE });` + ResourceId constant _tableId = ResourceId.wrap(0x7462776f726c640000000000000000004f7074696f6e616c53797374656d486f); + + FieldLayout constant _fieldLayout = + FieldLayout.wrap(0x0000000100000000000000000000000000000000000000000000000000000000); + + // Hex-encoded key schema of (address, bytes32, bytes32) + Schema constant _keySchema = Schema.wrap(0x00540300615f5f00000000000000000000000000000000000000000000000000); + // Hex-encoded value schema of (bytes21[]) + Schema constant _valueSchema = Schema.wrap(0x00000001b6000000000000000000000000000000000000000000000000000000); + + /** + * @notice Get the table's key field names. + * @return keyNames An array of strings with the names of key fields. + */ + function getKeyNames() internal pure returns (string[] memory keyNames) { + keyNames = new string[](3); + keyNames[0] = "player"; + keyNames[1] = "systemId"; + keyNames[2] = "callDataHash"; + } + + /** + * @notice Get the table's value field names. + * @return fieldNames An array of strings with the names of value fields. + */ + function getFieldNames() internal pure returns (string[] memory fieldNames) { + fieldNames = new string[](1); + fieldNames[0] = "hooks"; + } + + /** + * @notice Register the table with its config. + */ + function register() internal { + StoreSwitch.registerTable(_tableId, _fieldLayout, _keySchema, _valueSchema, getKeyNames(), getFieldNames()); + } + + /** + * @notice Register the table with its config. + */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, _keySchema, _valueSchema, getKeyNames(), getFieldNames()); + } + + /** + * @notice Register the table with its config (using the specified store). + */ + function register(IStore _store) internal { + _store.registerTable(_tableId, _fieldLayout, _keySchema, _valueSchema, getKeyNames(), getFieldNames()); + } + + /** + * @notice Get hooks. + */ + function getHooks( + address player, + ResourceId systemId, + bytes32 callDataHash + ) internal view returns (bytes21[] memory hooks) { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(player))); + _keyTuple[1] = ResourceId.unwrap(systemId); + _keyTuple[2] = callDataHash; + + bytes memory _blob = StoreSwitch.getDynamicField(_tableId, _keyTuple, 0); + return (SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_bytes21()); + } + + /** + * @notice Get hooks. + */ + function _getHooks( + address player, + ResourceId systemId, + bytes32 callDataHash + ) internal view returns (bytes21[] memory hooks) { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(player))); + _keyTuple[1] = ResourceId.unwrap(systemId); + _keyTuple[2] = callDataHash; + + bytes memory _blob = StoreCore.getDynamicField(_tableId, _keyTuple, 0); + return (SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_bytes21()); + } + + /** + * @notice Get hooks (using the specified store). + */ + function getHooks( + IStore _store, + address player, + ResourceId systemId, + bytes32 callDataHash + ) internal view returns (bytes21[] memory hooks) { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(player))); + _keyTuple[1] = ResourceId.unwrap(systemId); + _keyTuple[2] = callDataHash; + + bytes memory _blob = _store.getDynamicField(_tableId, _keyTuple, 0); + return (SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_bytes21()); + } + + /** + * @notice Get hooks. + */ + function get( + address player, + ResourceId systemId, + bytes32 callDataHash + ) internal view returns (bytes21[] memory hooks) { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(player))); + _keyTuple[1] = ResourceId.unwrap(systemId); + _keyTuple[2] = callDataHash; + + bytes memory _blob = StoreSwitch.getDynamicField(_tableId, _keyTuple, 0); + return (SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_bytes21()); + } + + /** + * @notice Get hooks. + */ + function _get( + address player, + ResourceId systemId, + bytes32 callDataHash + ) internal view returns (bytes21[] memory hooks) { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(player))); + _keyTuple[1] = ResourceId.unwrap(systemId); + _keyTuple[2] = callDataHash; + + bytes memory _blob = StoreCore.getDynamicField(_tableId, _keyTuple, 0); + return (SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_bytes21()); + } + + /** + * @notice Get hooks (using the specified store). + */ + function get( + IStore _store, + address player, + ResourceId systemId, + bytes32 callDataHash + ) internal view returns (bytes21[] memory hooks) { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(player))); + _keyTuple[1] = ResourceId.unwrap(systemId); + _keyTuple[2] = callDataHash; + + bytes memory _blob = _store.getDynamicField(_tableId, _keyTuple, 0); + return (SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_bytes21()); + } + + /** + * @notice Set hooks. + */ + function setHooks(address player, ResourceId systemId, bytes32 callDataHash, bytes21[] memory hooks) internal { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(player))); + _keyTuple[1] = ResourceId.unwrap(systemId); + _keyTuple[2] = callDataHash; + + StoreSwitch.setDynamicField(_tableId, _keyTuple, 0, EncodeArray.encode((hooks))); + } + + /** + * @notice Set hooks. + */ + function _setHooks(address player, ResourceId systemId, bytes32 callDataHash, bytes21[] memory hooks) internal { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(player))); + _keyTuple[1] = ResourceId.unwrap(systemId); + _keyTuple[2] = callDataHash; + + StoreCore.setDynamicField(_tableId, _keyTuple, 0, EncodeArray.encode((hooks))); + } + + /** + * @notice Set hooks (using the specified store). + */ + function setHooks( + IStore _store, + address player, + ResourceId systemId, + bytes32 callDataHash, + bytes21[] memory hooks + ) internal { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(player))); + _keyTuple[1] = ResourceId.unwrap(systemId); + _keyTuple[2] = callDataHash; + + _store.setDynamicField(_tableId, _keyTuple, 0, EncodeArray.encode((hooks))); + } + + /** + * @notice Set hooks. + */ + function set(address player, ResourceId systemId, bytes32 callDataHash, bytes21[] memory hooks) internal { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(player))); + _keyTuple[1] = ResourceId.unwrap(systemId); + _keyTuple[2] = callDataHash; + + StoreSwitch.setDynamicField(_tableId, _keyTuple, 0, EncodeArray.encode((hooks))); + } + + /** + * @notice Set hooks. + */ + function _set(address player, ResourceId systemId, bytes32 callDataHash, bytes21[] memory hooks) internal { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(player))); + _keyTuple[1] = ResourceId.unwrap(systemId); + _keyTuple[2] = callDataHash; + + StoreCore.setDynamicField(_tableId, _keyTuple, 0, EncodeArray.encode((hooks))); + } + + /** + * @notice Set hooks (using the specified store). + */ + function set( + IStore _store, + address player, + ResourceId systemId, + bytes32 callDataHash, + bytes21[] memory hooks + ) internal { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(player))); + _keyTuple[1] = ResourceId.unwrap(systemId); + _keyTuple[2] = callDataHash; + + _store.setDynamicField(_tableId, _keyTuple, 0, EncodeArray.encode((hooks))); + } + + /** + * @notice Get the length of hooks. + */ + function lengthHooks(address player, ResourceId systemId, bytes32 callDataHash) internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(player))); + _keyTuple[1] = ResourceId.unwrap(systemId); + _keyTuple[2] = callDataHash; + + uint256 _byteLength = StoreSwitch.getDynamicFieldLength(_tableId, _keyTuple, 0); + unchecked { + return _byteLength / 21; + } + } + + /** + * @notice Get the length of hooks. + */ + function _lengthHooks(address player, ResourceId systemId, bytes32 callDataHash) internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(player))); + _keyTuple[1] = ResourceId.unwrap(systemId); + _keyTuple[2] = callDataHash; + + uint256 _byteLength = StoreCore.getDynamicFieldLength(_tableId, _keyTuple, 0); + unchecked { + return _byteLength / 21; + } + } + + /** + * @notice Get the length of hooks (using the specified store). + */ + function lengthHooks( + IStore _store, + address player, + ResourceId systemId, + bytes32 callDataHash + ) internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(player))); + _keyTuple[1] = ResourceId.unwrap(systemId); + _keyTuple[2] = callDataHash; + + uint256 _byteLength = _store.getDynamicFieldLength(_tableId, _keyTuple, 0); + unchecked { + return _byteLength / 21; + } + } + + /** + * @notice Get the length of hooks. + */ + function length(address player, ResourceId systemId, bytes32 callDataHash) internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(player))); + _keyTuple[1] = ResourceId.unwrap(systemId); + _keyTuple[2] = callDataHash; + + uint256 _byteLength = StoreSwitch.getDynamicFieldLength(_tableId, _keyTuple, 0); + unchecked { + return _byteLength / 21; + } + } + + /** + * @notice Get the length of hooks. + */ + function _length(address player, ResourceId systemId, bytes32 callDataHash) internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(player))); + _keyTuple[1] = ResourceId.unwrap(systemId); + _keyTuple[2] = callDataHash; + + uint256 _byteLength = StoreCore.getDynamicFieldLength(_tableId, _keyTuple, 0); + unchecked { + return _byteLength / 21; + } + } + + /** + * @notice Get the length of hooks (using the specified store). + */ + function length( + IStore _store, + address player, + ResourceId systemId, + bytes32 callDataHash + ) internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(player))); + _keyTuple[1] = ResourceId.unwrap(systemId); + _keyTuple[2] = callDataHash; + + uint256 _byteLength = _store.getDynamicFieldLength(_tableId, _keyTuple, 0); + unchecked { + return _byteLength / 21; + } + } + + /** + * @notice Get an item of hooks. + * @dev Reverts with Store_IndexOutOfBounds if `_index` is out of bounds for the array. + */ + function getItemHooks( + address player, + ResourceId systemId, + bytes32 callDataHash, + uint256 _index + ) internal view returns (bytes21) { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(player))); + _keyTuple[1] = ResourceId.unwrap(systemId); + _keyTuple[2] = callDataHash; + + unchecked { + bytes memory _blob = StoreSwitch.getDynamicFieldSlice(_tableId, _keyTuple, 0, _index * 21, (_index + 1) * 21); + return (bytes21(_blob)); + } + } + + /** + * @notice Get an item of hooks. + * @dev Reverts with Store_IndexOutOfBounds if `_index` is out of bounds for the array. + */ + function _getItemHooks( + address player, + ResourceId systemId, + bytes32 callDataHash, + uint256 _index + ) internal view returns (bytes21) { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(player))); + _keyTuple[1] = ResourceId.unwrap(systemId); + _keyTuple[2] = callDataHash; + + unchecked { + bytes memory _blob = StoreCore.getDynamicFieldSlice(_tableId, _keyTuple, 0, _index * 21, (_index + 1) * 21); + return (bytes21(_blob)); + } + } + + /** + * @notice Get an item of hooks (using the specified store). + * @dev Reverts with Store_IndexOutOfBounds if `_index` is out of bounds for the array. + */ + function getItemHooks( + IStore _store, + address player, + ResourceId systemId, + bytes32 callDataHash, + uint256 _index + ) internal view returns (bytes21) { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(player))); + _keyTuple[1] = ResourceId.unwrap(systemId); + _keyTuple[2] = callDataHash; + + unchecked { + bytes memory _blob = _store.getDynamicFieldSlice(_tableId, _keyTuple, 0, _index * 21, (_index + 1) * 21); + return (bytes21(_blob)); + } + } + + /** + * @notice Get an item of hooks. + * @dev Reverts with Store_IndexOutOfBounds if `_index` is out of bounds for the array. + */ + function getItem( + address player, + ResourceId systemId, + bytes32 callDataHash, + uint256 _index + ) internal view returns (bytes21) { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(player))); + _keyTuple[1] = ResourceId.unwrap(systemId); + _keyTuple[2] = callDataHash; + + unchecked { + bytes memory _blob = StoreSwitch.getDynamicFieldSlice(_tableId, _keyTuple, 0, _index * 21, (_index + 1) * 21); + return (bytes21(_blob)); + } + } + + /** + * @notice Get an item of hooks. + * @dev Reverts with Store_IndexOutOfBounds if `_index` is out of bounds for the array. + */ + function _getItem( + address player, + ResourceId systemId, + bytes32 callDataHash, + uint256 _index + ) internal view returns (bytes21) { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(player))); + _keyTuple[1] = ResourceId.unwrap(systemId); + _keyTuple[2] = callDataHash; + + unchecked { + bytes memory _blob = StoreCore.getDynamicFieldSlice(_tableId, _keyTuple, 0, _index * 21, (_index + 1) * 21); + return (bytes21(_blob)); + } + } + + /** + * @notice Get an item of hooks (using the specified store). + * @dev Reverts with Store_IndexOutOfBounds if `_index` is out of bounds for the array. + */ + function getItem( + IStore _store, + address player, + ResourceId systemId, + bytes32 callDataHash, + uint256 _index + ) internal view returns (bytes21) { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(player))); + _keyTuple[1] = ResourceId.unwrap(systemId); + _keyTuple[2] = callDataHash; + + unchecked { + bytes memory _blob = _store.getDynamicFieldSlice(_tableId, _keyTuple, 0, _index * 21, (_index + 1) * 21); + return (bytes21(_blob)); + } + } + + /** + * @notice Push an element to hooks. + */ + function pushHooks(address player, ResourceId systemId, bytes32 callDataHash, bytes21 _element) internal { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(player))); + _keyTuple[1] = ResourceId.unwrap(systemId); + _keyTuple[2] = callDataHash; + + StoreSwitch.pushToDynamicField(_tableId, _keyTuple, 0, abi.encodePacked((_element))); + } + + /** + * @notice Push an element to hooks. + */ + function _pushHooks(address player, ResourceId systemId, bytes32 callDataHash, bytes21 _element) internal { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(player))); + _keyTuple[1] = ResourceId.unwrap(systemId); + _keyTuple[2] = callDataHash; + + StoreCore.pushToDynamicField(_tableId, _keyTuple, 0, abi.encodePacked((_element))); + } + + /** + * @notice Push an element to hooks (using the specified store). + */ + function pushHooks( + IStore _store, + address player, + ResourceId systemId, + bytes32 callDataHash, + bytes21 _element + ) internal { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(player))); + _keyTuple[1] = ResourceId.unwrap(systemId); + _keyTuple[2] = callDataHash; + + _store.pushToDynamicField(_tableId, _keyTuple, 0, abi.encodePacked((_element))); + } + + /** + * @notice Push an element to hooks. + */ + function push(address player, ResourceId systemId, bytes32 callDataHash, bytes21 _element) internal { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(player))); + _keyTuple[1] = ResourceId.unwrap(systemId); + _keyTuple[2] = callDataHash; + + StoreSwitch.pushToDynamicField(_tableId, _keyTuple, 0, abi.encodePacked((_element))); + } + + /** + * @notice Push an element to hooks. + */ + function _push(address player, ResourceId systemId, bytes32 callDataHash, bytes21 _element) internal { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(player))); + _keyTuple[1] = ResourceId.unwrap(systemId); + _keyTuple[2] = callDataHash; + + StoreCore.pushToDynamicField(_tableId, _keyTuple, 0, abi.encodePacked((_element))); + } + + /** + * @notice Push an element to hooks (using the specified store). + */ + function push(IStore _store, address player, ResourceId systemId, bytes32 callDataHash, bytes21 _element) internal { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(player))); + _keyTuple[1] = ResourceId.unwrap(systemId); + _keyTuple[2] = callDataHash; + + _store.pushToDynamicField(_tableId, _keyTuple, 0, abi.encodePacked((_element))); + } + + /** + * @notice Pop an element from hooks. + */ + function popHooks(address player, ResourceId systemId, bytes32 callDataHash) internal { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(player))); + _keyTuple[1] = ResourceId.unwrap(systemId); + _keyTuple[2] = callDataHash; + + StoreSwitch.popFromDynamicField(_tableId, _keyTuple, 0, 21); + } + + /** + * @notice Pop an element from hooks. + */ + function _popHooks(address player, ResourceId systemId, bytes32 callDataHash) internal { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(player))); + _keyTuple[1] = ResourceId.unwrap(systemId); + _keyTuple[2] = callDataHash; + + StoreCore.popFromDynamicField(_tableId, _keyTuple, 0, 21); + } + + /** + * @notice Pop an element from hooks (using the specified store). + */ + function popHooks(IStore _store, address player, ResourceId systemId, bytes32 callDataHash) internal { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(player))); + _keyTuple[1] = ResourceId.unwrap(systemId); + _keyTuple[2] = callDataHash; + + _store.popFromDynamicField(_tableId, _keyTuple, 0, 21); + } + + /** + * @notice Pop an element from hooks. + */ + function pop(address player, ResourceId systemId, bytes32 callDataHash) internal { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(player))); + _keyTuple[1] = ResourceId.unwrap(systemId); + _keyTuple[2] = callDataHash; + + StoreSwitch.popFromDynamicField(_tableId, _keyTuple, 0, 21); + } + + /** + * @notice Pop an element from hooks. + */ + function _pop(address player, ResourceId systemId, bytes32 callDataHash) internal { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(player))); + _keyTuple[1] = ResourceId.unwrap(systemId); + _keyTuple[2] = callDataHash; + + StoreCore.popFromDynamicField(_tableId, _keyTuple, 0, 21); + } + + /** + * @notice Pop an element from hooks (using the specified store). + */ + function pop(IStore _store, address player, ResourceId systemId, bytes32 callDataHash) internal { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(player))); + _keyTuple[1] = ResourceId.unwrap(systemId); + _keyTuple[2] = callDataHash; + + _store.popFromDynamicField(_tableId, _keyTuple, 0, 21); + } + + /** + * @notice Update an element of hooks at `_index`. + */ + function updateHooks( + address player, + ResourceId systemId, + bytes32 callDataHash, + uint256 _index, + bytes21 _element + ) internal { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(player))); + _keyTuple[1] = ResourceId.unwrap(systemId); + _keyTuple[2] = callDataHash; + + unchecked { + bytes memory _encoded = abi.encodePacked((_element)); + StoreSwitch.spliceDynamicData(_tableId, _keyTuple, 0, uint40(_index * 21), uint40(_encoded.length), _encoded); + } + } + + /** + * @notice Update an element of hooks at `_index`. + */ + function _updateHooks( + address player, + ResourceId systemId, + bytes32 callDataHash, + uint256 _index, + bytes21 _element + ) internal { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(player))); + _keyTuple[1] = ResourceId.unwrap(systemId); + _keyTuple[2] = callDataHash; + + unchecked { + bytes memory _encoded = abi.encodePacked((_element)); + StoreCore.spliceDynamicData(_tableId, _keyTuple, 0, uint40(_index * 21), uint40(_encoded.length), _encoded); + } + } + + /** + * @notice Update an element of hooks (using the specified store) at `_index`. + */ + function updateHooks( + IStore _store, + address player, + ResourceId systemId, + bytes32 callDataHash, + uint256 _index, + bytes21 _element + ) internal { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(player))); + _keyTuple[1] = ResourceId.unwrap(systemId); + _keyTuple[2] = callDataHash; + + unchecked { + bytes memory _encoded = abi.encodePacked((_element)); + _store.spliceDynamicData(_tableId, _keyTuple, 0, uint40(_index * 21), uint40(_encoded.length), _encoded); + } + } + + /** + * @notice Update an element of hooks at `_index`. + */ + function update( + address player, + ResourceId systemId, + bytes32 callDataHash, + uint256 _index, + bytes21 _element + ) internal { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(player))); + _keyTuple[1] = ResourceId.unwrap(systemId); + _keyTuple[2] = callDataHash; + + unchecked { + bytes memory _encoded = abi.encodePacked((_element)); + StoreSwitch.spliceDynamicData(_tableId, _keyTuple, 0, uint40(_index * 21), uint40(_encoded.length), _encoded); + } + } + + /** + * @notice Update an element of hooks at `_index`. + */ + function _update( + address player, + ResourceId systemId, + bytes32 callDataHash, + uint256 _index, + bytes21 _element + ) internal { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(player))); + _keyTuple[1] = ResourceId.unwrap(systemId); + _keyTuple[2] = callDataHash; + + unchecked { + bytes memory _encoded = abi.encodePacked((_element)); + StoreCore.spliceDynamicData(_tableId, _keyTuple, 0, uint40(_index * 21), uint40(_encoded.length), _encoded); + } + } + + /** + * @notice Update an element of hooks (using the specified store) at `_index`. + */ + function update( + IStore _store, + address player, + ResourceId systemId, + bytes32 callDataHash, + uint256 _index, + bytes21 _element + ) internal { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(player))); + _keyTuple[1] = ResourceId.unwrap(systemId); + _keyTuple[2] = callDataHash; + + unchecked { + bytes memory _encoded = abi.encodePacked((_element)); + _store.spliceDynamicData(_tableId, _keyTuple, 0, uint40(_index * 21), uint40(_encoded.length), _encoded); + } + } + + /** + * @notice Delete all data for given keys. + */ + function deleteRecord(address player, ResourceId systemId, bytes32 callDataHash) internal { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(player))); + _keyTuple[1] = ResourceId.unwrap(systemId); + _keyTuple[2] = callDataHash; + + StoreSwitch.deleteRecord(_tableId, _keyTuple); + } + + /** + * @notice Delete all data for given keys. + */ + function _deleteRecord(address player, ResourceId systemId, bytes32 callDataHash) internal { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(player))); + _keyTuple[1] = ResourceId.unwrap(systemId); + _keyTuple[2] = callDataHash; + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /** + * @notice Delete all data for given keys (using the specified store). + */ + function deleteRecord(IStore _store, address player, ResourceId systemId, bytes32 callDataHash) internal { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(player))); + _keyTuple[1] = ResourceId.unwrap(systemId); + _keyTuple[2] = callDataHash; + + _store.deleteRecord(_tableId, _keyTuple); + } + + /** + * @notice Tightly pack dynamic data lengths using this table's schema. + * @return _encodedLengths The lengths of the dynamic fields (packed into a single bytes32 value). + */ + function encodeLengths(bytes21[] memory hooks) internal pure returns (EncodedLengths _encodedLengths) { + // Lengths are effectively checked during copy by 2**40 bytes exceeding gas limits + unchecked { + _encodedLengths = EncodedLengthsLib.pack(hooks.length * 21); + } + } + + /** + * @notice Tightly pack dynamic (variable length) data using this table's schema. + * @return The dynamic data, encoded into a sequence of bytes. + */ + function encodeDynamic(bytes21[] memory hooks) internal pure returns (bytes memory) { + return abi.encodePacked(EncodeArray.encode((hooks))); + } + + /** + * @notice Encode all of a record's fields. + * @return The static (fixed length) data, encoded into a sequence of bytes. + * @return The lengths of the dynamic fields (packed into a single bytes32 value). + * @return The dynamic (variable length) data, encoded into a sequence of bytes. + */ + function encode(bytes21[] memory hooks) internal pure returns (bytes memory, EncodedLengths, bytes memory) { + bytes memory _staticData; + EncodedLengths _encodedLengths = encodeLengths(hooks); + bytes memory _dynamicData = encodeDynamic(hooks); + + return (_staticData, _encodedLengths, _dynamicData); + } + + /** + * @notice Encode keys as a bytes32 array using this table's field layout. + */ + function encodeKeyTuple( + address player, + ResourceId systemId, + bytes32 callDataHash + ) internal pure returns (bytes32[] memory) { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(player))); + _keyTuple[1] = ResourceId.unwrap(systemId); + _keyTuple[2] = callDataHash; + + return _keyTuple; + } +} diff --git a/packages/world/src/codegen/tables/UserDelegationControl.sol b/packages/world/src/codegen/tables/UserDelegationControl.sol index adb06d1d62..8eb8e5dfe4 100644 --- a/packages/world/src/codegen/tables/UserDelegationControl.sol +++ b/packages/world/src/codegen/tables/UserDelegationControl.sol @@ -64,6 +64,13 @@ library UserDelegationControl { StoreCore.registerTable(_tableId, _fieldLayout, _keySchema, _valueSchema, getKeyNames(), getFieldNames()); } + /** + * @notice Register the table with its config (using the specified store). + */ + function register(IStore _store) internal { + _store.registerTable(_tableId, _fieldLayout, _keySchema, _valueSchema, getKeyNames(), getFieldNames()); + } + /** * @notice Get delegationControlId. */ @@ -94,6 +101,22 @@ library UserDelegationControl { return ResourceId.wrap(bytes32(_blob)); } + /** + * @notice Get delegationControlId (using the specified store). + */ + function getDelegationControlId( + IStore _store, + address delegator, + address delegatee + ) internal view returns (ResourceId delegationControlId) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(uint160(delegator))); + _keyTuple[1] = bytes32(uint256(uint160(delegatee))); + + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return ResourceId.wrap(bytes32(_blob)); + } + /** * @notice Get delegationControlId. */ @@ -118,6 +141,22 @@ library UserDelegationControl { return ResourceId.wrap(bytes32(_blob)); } + /** + * @notice Get delegationControlId (using the specified store). + */ + function get( + IStore _store, + address delegator, + address delegatee + ) internal view returns (ResourceId delegationControlId) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(uint160(delegator))); + _keyTuple[1] = bytes32(uint256(uint160(delegatee))); + + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return ResourceId.wrap(bytes32(_blob)); + } + /** * @notice Set delegationControlId. */ @@ -152,6 +191,28 @@ library UserDelegationControl { ); } + /** + * @notice Set delegationControlId (using the specified store). + */ + function setDelegationControlId( + IStore _store, + address delegator, + address delegatee, + ResourceId delegationControlId + ) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(uint160(delegator))); + _keyTuple[1] = bytes32(uint256(uint160(delegatee))); + + _store.setStaticField( + _tableId, + _keyTuple, + 0, + abi.encodePacked(ResourceId.unwrap(delegationControlId)), + _fieldLayout + ); + } + /** * @notice Set delegationControlId. */ @@ -186,6 +247,23 @@ library UserDelegationControl { ); } + /** + * @notice Set delegationControlId (using the specified store). + */ + function set(IStore _store, address delegator, address delegatee, ResourceId delegationControlId) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(uint160(delegator))); + _keyTuple[1] = bytes32(uint256(uint160(delegatee))); + + _store.setStaticField( + _tableId, + _keyTuple, + 0, + abi.encodePacked(ResourceId.unwrap(delegationControlId)), + _fieldLayout + ); + } + /** * @notice Delete all data for given keys. */ @@ -208,6 +286,17 @@ library UserDelegationControl { StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); } + /** + * @notice Delete all data for given keys (using the specified store). + */ + function deleteRecord(IStore _store, address delegator, address delegatee) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(uint160(delegator))); + _keyTuple[1] = bytes32(uint256(uint160(delegatee))); + + _store.deleteRecord(_tableId, _keyTuple); + } + /** * @notice Tightly pack static (fixed length) data using this table's schema. * @return The static data, encoded into a sequence of bytes. diff --git a/packages/world/src/modules/init/ExtendedRegistrationSystem.sol b/packages/world/src/modules/init/ExtendedRegistrationSystem.sol new file mode 100644 index 0000000000..2f45e2f158 --- /dev/null +++ b/packages/world/src/modules/init/ExtendedRegistrationSystem.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.24; + +import { IWorldErrors } from "../../IWorldErrors.sol"; + +import { ExtendedWorldRegistrationSystem } from "./implementations/ExtendedWorldRegistrationSystem.sol"; + +/** + * @title Extended Registration System for World + * @author MUD (https://mud.dev) by Lattice (https://lattice.xyz) + * @notice This system aggregates World registration and installation functionalities externalized from the World contract, aiming to keep the World contract's bytecode lean. + * @dev Aggregates multiple system implementations for the World. + */ +contract ExtendedRegistrationSystem is ExtendedWorldRegistrationSystem { + // Currently, no additional functionality is added in this aggregate contract. +} diff --git a/packages/world/src/modules/init/InitModule.sol b/packages/world/src/modules/init/InitModule.sol index f2085d0c34..5b2ba60ace 100644 --- a/packages/world/src/modules/init/InitModule.sol +++ b/packages/world/src/modules/init/InitModule.sol @@ -21,13 +21,16 @@ import { BalanceTransferSystem } from "./implementations/BalanceTransferSystem.s import { BatchCallSystem } from "./implementations/BatchCallSystem.sol"; import { RegistrationSystem } from "./RegistrationSystem.sol"; -import { ACCESS_MANAGEMENT_SYSTEM_ID, BALANCE_TRANSFER_SYSTEM_ID, BATCH_CALL_SYSTEM_ID, REGISTRATION_SYSTEM_ID } from "./constants.sol"; -import { getFunctionSignaturesAccessManagement, getFunctionSignaturesBalanceTransfer, getFunctionSignaturesBatchCall, getFunctionSignaturesRegistration } from "./functionSignatures.sol"; +import { ExtendedRegistrationSystem } from "./ExtendedRegistrationSystem.sol"; + +import { ACCESS_MANAGEMENT_SYSTEM_ID, BALANCE_TRANSFER_SYSTEM_ID, BATCH_CALL_SYSTEM_ID, REGISTRATION_SYSTEM_ID, EXTENDED_REGISTRATION_SYSTEM_ID } from "./constants.sol"; +import { getFunctionSignaturesAccessManagement, getFunctionSignaturesBalanceTransfer, getFunctionSignaturesBatchCall, getFunctionSignaturesRegistration, getFunctionSignaturesExtendedRegistration } from "./functionSignatures.sol"; import { Systems } from "../../codegen/tables/Systems.sol"; import { FunctionSelectors } from "../../codegen/tables/FunctionSelectors.sol"; import { FunctionSignatures } from "../../codegen/tables/FunctionSignatures.sol"; import { SystemHooks } from "../../codegen/tables/SystemHooks.sol"; +import { OptionalSystemHooks } from "../../codegen/tables/OptionalSystemHooks.sol"; import { SystemRegistry } from "../../codegen/tables/SystemRegistry.sol"; import { InitModuleAddress } from "../../codegen/tables/InitModuleAddress.sol"; import { Balances } from "../../codegen/tables/Balances.sol"; @@ -45,17 +48,20 @@ contract InitModule is Module { address internal immutable balanceTransferSystem; address internal immutable batchCallSystem; address internal immutable registrationSystem; + address internal immutable extendedRegistrationSystem; constructor( AccessManagementSystem _accessManagementSystem, BalanceTransferSystem _balanceTransferSystem, BatchCallSystem _batchCallSystem, - RegistrationSystem _registrationSystem + RegistrationSystem _registrationSystem, + ExtendedRegistrationSystem _extendedRegistrationSystem ) { accessManagementSystem = address(_accessManagementSystem); balanceTransferSystem = address(_balanceTransferSystem); batchCallSystem = address(_batchCallSystem); registrationSystem = address(_registrationSystem); + extendedRegistrationSystem = address(_extendedRegistrationSystem); } /** @@ -92,6 +98,7 @@ contract InitModule is Module { FunctionSelectors.register(); FunctionSignatures.register(); SystemHooks.register(); + OptionalSystemHooks.register(); SystemRegistry.register(); InitModuleAddress.register(); @@ -116,6 +123,7 @@ contract InitModule is Module { _registerSystem(balanceTransferSystem, BALANCE_TRANSFER_SYSTEM_ID); _registerSystem(batchCallSystem, BATCH_CALL_SYSTEM_ID); _registerSystem(registrationSystem, REGISTRATION_SYSTEM_ID); + _registerSystem(extendedRegistrationSystem, EXTENDED_REGISTRATION_SYSTEM_ID); } /** @@ -155,6 +163,11 @@ contract InitModule is Module { for (uint256 i = 0; i < functionSignaturesRegistration.length; i++) { _registerRootFunctionSelector(REGISTRATION_SYSTEM_ID, functionSignaturesRegistration[i]); } + + string[2] memory functionSignaturesExtendedRegistration = getFunctionSignaturesExtendedRegistration(); + for (uint256 i = 0; i < functionSignaturesExtendedRegistration.length; i++) { + _registerRootFunctionSelector(EXTENDED_REGISTRATION_SYSTEM_ID, functionSignaturesExtendedRegistration[i]); + } } /** diff --git a/packages/world/src/modules/init/constants.sol b/packages/world/src/modules/init/constants.sol index e17ab3635a..39c2c37f35 100644 --- a/packages/world/src/modules/init/constants.sol +++ b/packages/world/src/modules/init/constants.sol @@ -37,3 +37,11 @@ ResourceId constant BATCH_CALL_SYSTEM_ID = ResourceId.wrap( ResourceId constant REGISTRATION_SYSTEM_ID = ResourceId.wrap( bytes32(abi.encodePacked(RESOURCE_SYSTEM, ROOT_NAMESPACE, bytes16("Registration"))) ); + +/** + * @dev Resource ID for extended core registration system. + * @dev This ID is derived from the RESOURCE_SYSTEM type, the ROOT_NAMESPACE, and the system name. + */ +ResourceId constant EXTENDED_REGISTRATION_SYSTEM_ID = ResourceId.wrap( + bytes32(abi.encodePacked(RESOURCE_SYSTEM, ROOT_NAMESPACE, bytes16("ExtendedRegistra"))) +); diff --git a/packages/world/src/modules/init/functionSignatures.sol b/packages/world/src/modules/init/functionSignatures.sol index e9a7fe13bd..122a7c99a8 100644 --- a/packages/world/src/modules/init/functionSignatures.sol +++ b/packages/world/src/modules/init/functionSignatures.sol @@ -60,3 +60,14 @@ function getFunctionSignaturesRegistration() pure returns (string[14] memory) { "unregisterNamespaceDelegation(bytes32)" ]; } + +/** + * @dev Function signatures for extended registration system + */ +function getFunctionSignaturesExtendedRegistration() pure returns (string[2] memory) { + return [ + // --- ExtendedWorldRegistrationSystem --- + "registerOptionalSystemHook(bytes32,address,uint8,bytes32)", + "unregisterOptionalSystemHook(bytes32,address,bytes32)" + ]; +} diff --git a/packages/world/src/modules/init/implementations/ExtendedWorldRegistrationSystem.sol b/packages/world/src/modules/init/implementations/ExtendedWorldRegistrationSystem.sol new file mode 100644 index 0000000000..55dc897922 --- /dev/null +++ b/packages/world/src/modules/init/implementations/ExtendedWorldRegistrationSystem.sol @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.24; + +import { Hook, HookLib } from "@latticexyz/store/src/Hook.sol"; +import { ResourceId } from "@latticexyz/store/src/ResourceId.sol"; +import { ResourceIds } from "@latticexyz/store/src/codegen/tables/ResourceIds.sol"; + +import { System } from "../../../System.sol"; +import { WorldContextConsumer, IWorldContextConsumer } from "../../../WorldContext.sol"; +import { WorldResourceIdLib, WorldResourceIdInstance } from "../../../WorldResourceId.sol"; +import { SystemCall } from "../../../SystemCall.sol"; +import { ROOT_NAMESPACE_ID, ROOT_NAME } from "../../../constants.sol"; +import { RESOURCE_NAMESPACE, RESOURCE_SYSTEM } from "../../../worldResourceTypes.sol"; +import { AccessControl } from "../../../AccessControl.sol"; +import { Delegation } from "../../../Delegation.sol"; +import { requireInterface } from "../../../requireInterface.sol"; +import { NamespaceOwner } from "../../../codegen/tables/NamespaceOwner.sol"; +import { ResourceAccess } from "../../../codegen/tables/ResourceAccess.sol"; +import { UserDelegationControl } from "../../../codegen/tables/UserDelegationControl.sol"; +import { NamespaceDelegationControl } from "../../../codegen/tables/NamespaceDelegationControl.sol"; +import { IOptionalSystemHook } from "../../../IOptionalSystemHook.sol"; +import { IWorldErrors } from "../../../IWorldErrors.sol"; +import { IDelegationControl } from "../../../IDelegationControl.sol"; +import { ICustomUnregisterDelegation } from "../../../ICustomUnregisterDelegation.sol"; +import { ERC165Checker } from "../../../ERC165Checker.sol"; + +import { SystemHooks } from "../../../codegen/tables/SystemHooks.sol"; +import { OptionalSystemHooks } from "../../../codegen/tables/OptionalSystemHooks.sol"; +import { SystemRegistry } from "../../../codegen/tables/SystemRegistry.sol"; +import { Systems } from "../../../codegen/tables/Systems.sol"; +import { FunctionSelectors } from "../../../codegen/tables/FunctionSelectors.sol"; +import { FunctionSignatures } from "../../../codegen/tables/FunctionSignatures.sol"; +import { requireNamespace } from "../../../requireNamespace.sol"; +import { requireValidNamespace } from "../../../requireValidNamespace.sol"; + +import { LimitedCallContext } from "../LimitedCallContext.sol"; + +/** + * @title ExtendedWorldRegistrationSystem + * @dev This contract provides extended functions related to registering resources other than tables in the World. + */ +contract ExtendedWorldRegistrationSystem is System, IWorldErrors, LimitedCallContext { + using WorldResourceIdInstance for ResourceId; + + /** + * @notice Registers a new optional system hook for the user + * @dev Adds a new hook for the system at the provided user, system, and call data hash (optional) + * @param systemId The ID of the system + * @param hookAddress The address of the hook being registered + * @param enabledHooksBitmap Bitmap indicating which hooks are enabled + * @param callDataHash The hash of the call data for the system hook + */ + function registerOptionalSystemHook( + ResourceId systemId, + IOptionalSystemHook hookAddress, + uint8 enabledHooksBitmap, + bytes32 callDataHash + ) public onlyDelegatecall { + // Require the provided system ID to have type RESOURCE_SYSTEM + if (systemId.getType() != RESOURCE_SYSTEM) { + revert World_InvalidResourceType(RESOURCE_SYSTEM, systemId, systemId.toString()); + } + + // Require the provided address to implement the IOptionalSystemHook interface + requireInterface(address(hookAddress), type(IOptionalSystemHook).interfaceId); + + // Require the system to exist + AccessControl.requireExistence(systemId); + + // Require the system's namespace to exist + AccessControl.requireExistence(systemId.getNamespaceId()); + + bytes21[] memory currentHooks = OptionalSystemHooks._get(_msgSender(), systemId, callDataHash); + for (uint256 i = 0; i < currentHooks.length; i++) { + if (Hook.wrap(currentHooks[i]).getAddress() == address(hookAddress)) { + revert World_OptionalHookAlreadyRegistered(systemId, address(hookAddress), callDataHash); + } + } + + IOptionalSystemHook(address(hookAddress)).onRegisterHook(_msgSender(), systemId, enabledHooksBitmap, callDataHash); + + // Register the hook + OptionalSystemHooks.push( + _msgSender(), + systemId, + callDataHash, + Hook.unwrap(HookLib.encode(address(hookAddress), enabledHooksBitmap)) + ); + } + + /** + * @notice Unregisters an optional system hook + * @dev Removes a hook for the system at the provided user, system, and call data hash (optional) + * @param systemId The ID of the system + * @param hookAddress The address of the hook being unregistered + * @param callDataHash The hash of the call data for the system hook + */ + function unregisterOptionalSystemHook( + ResourceId systemId, + IOptionalSystemHook hookAddress, + bytes32 callDataHash + ) public virtual onlyDelegatecall { + // Remove the hook from the list of hooks for this system in the optional system hooks table + bytes21[] memory currentHooks = OptionalSystemHooks._get(_msgSender(), systemId, callDataHash); + + // Initialize the new hooks array with the same length because we don't know if the hook is registered yet + bytes21[] memory newHooks = new bytes21[](currentHooks.length); + + // Filter the array of current hooks + uint256 newHooksIndex; + unchecked { + for (uint256 currentHooksIndex; currentHooksIndex < currentHooks.length; currentHooksIndex++) { + Hook hook = Hook.wrap(currentHooks[currentHooksIndex]); + if (hook.getAddress() != address(hookAddress)) { + newHooks[newHooksIndex] = currentHooks[currentHooksIndex]; + newHooksIndex++; + } else { + address(hookAddress).call( + abi.encodeCall( + IOptionalSystemHook.onUnregisterHook, + (_msgSender(), systemId, hook.getBitmap(), callDataHash) + ) + ); + } + } + } + + // Set the new hooks table length in place + // (Note: this does not update the free memory pointer) + assembly { + mstore(newHooks, newHooksIndex) + } + + // Set the new hooks table + OptionalSystemHooks._set(_msgSender(), systemId, callDataHash, newHooks); + } +} diff --git a/packages/world/src/modules/init/implementations/WorldRegistrationSystem.sol b/packages/world/src/modules/init/implementations/WorldRegistrationSystem.sol index bd91822a85..2c2cf8ecda 100644 --- a/packages/world/src/modules/init/implementations/WorldRegistrationSystem.sol +++ b/packages/world/src/modules/init/implementations/WorldRegistrationSystem.sol @@ -21,6 +21,8 @@ import { NamespaceDelegationControl } from "../../../codegen/tables/NamespaceDel import { ISystemHook } from "../../../ISystemHook.sol"; import { IWorldErrors } from "../../../IWorldErrors.sol"; import { IDelegationControl } from "../../../IDelegationControl.sol"; +import { ICustomUnregisterDelegation } from "../../../ICustomUnregisterDelegation.sol"; +import { ERC165Checker } from "../../../ERC165Checker.sol"; import { SystemHooks } from "../../../codegen/tables/SystemHooks.sol"; import { SystemRegistry } from "../../../codegen/tables/SystemRegistry.sol"; @@ -274,6 +276,15 @@ contract WorldRegistrationSystem is System, IWorldErrors, LimitedCallContext { * @param delegatee The address of the delegatee */ function unregisterDelegation(address delegatee) public onlyDelegatecall { + if (ERC165Checker.supportsInterface(delegatee, type(ICustomUnregisterDelegation).interfaceId)) { + (bool canUnregisterSuccess, bytes memory canUnregisterReturnData) = delegatee.call( + abi.encodeCall(ICustomUnregisterDelegation.canUnregister, (_msgSender())) + ); + if (!canUnregisterSuccess) revert World_CustomUnregisterDelegationNotAllowed(); + canUnregisterSuccess = abi.decode(canUnregisterReturnData, (bool)); + if (!canUnregisterSuccess) revert World_CustomUnregisterDelegationNotAllowed(); + } + // Delete the delegation control contract address UserDelegationControl.deleteRecord({ delegator: _msgSender(), delegatee: delegatee }); } diff --git a/packages/world/test/createInitModule.sol b/packages/world/test/createInitModule.sol index 8354584c5a..055d243324 100644 --- a/packages/world/test/createInitModule.sol +++ b/packages/world/test/createInitModule.sol @@ -7,6 +7,7 @@ import { BatchCallSystem } from "../src/modules/init/implementations/BatchCallSy import { InitModule } from "../src/modules/init/InitModule.sol"; import { RegistrationSystem } from "../src/modules/init/RegistrationSystem.sol"; +import { ExtendedRegistrationSystem } from "../src/modules/init/ExtendedRegistrationSystem.sol"; function createInitModule() returns (InitModule) { return @@ -14,6 +15,7 @@ function createInitModule() returns (InitModule) { new AccessManagementSystem(), new BalanceTransferSystem(), new BatchCallSystem(), - new RegistrationSystem() + new RegistrationSystem(), + new ExtendedRegistrationSystem() ); } From 19469595a1c54fc25b7d889f54faf439d3d7e09a Mon Sep 17 00:00:00 2001 From: Dhvani Patel Date: Thu, 4 Jul 2024 11:56:06 +0100 Subject: [PATCH 2/8] fix: gas buffer; indexer skipping blocks; support batch call --- .../src/createBlockStream.ts | 1 + packages/block-logs-stream/src/fetchLogs.ts | 13 +++++++- packages/common/src/writeContract.ts | 5 ++++ packages/store-sync/src/createStoreSync.ts | 30 ++++++++++++++----- packages/world/ts/actions/callFrom.ts | 21 +++++++++++++ 5 files changed, 61 insertions(+), 9 deletions(-) diff --git a/packages/block-logs-stream/src/createBlockStream.ts b/packages/block-logs-stream/src/createBlockStream.ts index 7fa6f82b46..b974439de3 100644 --- a/packages/block-logs-stream/src/createBlockStream.ts +++ b/packages/block-logs-stream/src/createBlockStream.ts @@ -16,6 +16,7 @@ export function createBlockStream({ return publicClient.watchBlocks({ blockTag, emitOnBegin: true, + emitMissed: true, onBlock: (block) => subscriber.next(block), onError: (error) => subscriber.error(error), }); diff --git a/packages/block-logs-stream/src/fetchLogs.ts b/packages/block-logs-stream/src/fetchLogs.ts index d6de091619..34fd9d5657 100644 --- a/packages/block-logs-stream/src/fetchLogs.ts +++ b/packages/block-logs-stream/src/fetchLogs.ts @@ -90,7 +90,18 @@ export async function* fetchLogs({ try { const toBlock = fromBlock + blockRange; debug("getting logs", { fromBlock, toBlock }); - const logs = await publicClient.getLogs({ ...getLogsOpts, fromBlock, toBlock, strict: true }); + + const [latestBlockNumber, logs] = await Promise.all([ + publicClient.getBlockNumber({ cacheTime: 0 }), + publicClient.getLogs({ ...getLogsOpts, fromBlock, toBlock, strict: true }), + ]); + if (latestBlockNumber < toBlock) { + const blockTimeInSeconds = 2; + const seconds = Number(toBlock - latestBlockNumber) * blockTimeInSeconds; + debug(`latest block number ${latestBlockNumber} is less than toBlock ${toBlock}, retrying in ${seconds}s`); + await wait(1000 * seconds); + continue; + } yield { fromBlock, toBlock, logs }; fromBlock = toBlock + 1n; blockRange = bigIntMin(maxBlockRange, getLogsOpts.toBlock - fromBlock); diff --git a/packages/common/src/writeContract.ts b/packages/common/src/writeContract.ts index 7851a09b8e..0da2f24662 100644 --- a/packages/common/src/writeContract.ts +++ b/packages/common/src/writeContract.ts @@ -116,6 +116,11 @@ export async function writeContract< to: address, } as never); + // add 20% buffer to the gas estimate + if (preparedTransaction.gas) { + preparedTransaction.gas = (preparedTransaction.gas * 120n) / 100n; + } + return preparedTransaction as never; } diff --git a/packages/store-sync/src/createStoreSync.ts b/packages/store-sync/src/createStoreSync.ts index a359d8d813..c04b77c9d7 100644 --- a/packages/store-sync/src/createStoreSync.ts +++ b/packages/store-sync/src/createStoreSync.ts @@ -26,6 +26,7 @@ import { shareReplay, combineLatest, scan, + retry, mergeMap, } from "rxjs"; import { debug as parentDebug } from "./debug"; @@ -187,19 +188,26 @@ export async function createStoreSync( tap((startBlock) => debug("starting sync from block", startBlock)), ); + let startBlock: bigint | null = null; + let endBlock: bigint | null = null; + let lastBlockNumberProcessed: bigint | null = null; + const latestBlock$ = createBlockStream({ publicClient, blockTag: followBlockTag }).pipe(shareReplay(1)); const latestBlockNumber$ = latestBlock$.pipe( map((block) => block.number), tap((blockNumber) => { debug("on block number", blockNumber, "for", followBlockTag, "block tag"); }), + retry({ + count: undefined, // ie unlimited + delay: 3000, // poll the RPC every 3 seconds + }), + filter((blockNumber) => { + return lastBlockNumberProcessed == null || blockNumber > lastBlockNumberProcessed; + }), shareReplay(1), ); - let startBlock: bigint | null = null; - let endBlock: bigint | null = null; - let lastBlockNumberProcessed: bigint | null = null; - const storedBlock$ = combineLatest([startBlock$, latestBlockNumber$]).pipe( map(([startBlock, endBlock]) => ({ startBlock, endBlock })), tap((range) => { @@ -207,15 +215,21 @@ export async function createStoreSync( endBlock = range.endBlock; }), concatMap((range) => { + const fromBlock = lastBlockNumberProcessed + ? bigIntMax(range.startBlock, lastBlockNumberProcessed + 1n) + : range.startBlock; + const toBlock = range.endBlock; + if (toBlock < fromBlock) { + throw new Error(`toBlock ${toBlock} is less than fromBlock ${fromBlock}`); + } + const storedBlocks = fetchAndStoreLogs({ publicClient, address, events: storeEventsAbi, maxBlockRange, - fromBlock: lastBlockNumberProcessed - ? bigIntMax(range.startBlock, lastBlockNumberProcessed + 1n) - : range.startBlock, - toBlock: range.endBlock, + fromBlock: fromBlock, + toBlock: toBlock, storageAdapter, logFilter, }); diff --git a/packages/world/ts/actions/callFrom.ts b/packages/world/ts/actions/callFrom.ts index e4335d9e52..1f4f583496 100644 --- a/packages/world/ts/actions/callFrom.ts +++ b/packages/world/ts/actions/callFrom.ts @@ -55,11 +55,32 @@ export function callFrom( writeArgs.address !== params.worldAddress || writeArgs.functionName === "call" || writeArgs.functionName === "callFrom" || + writeArgs.functionName === "batchCallFrom" || writeArgs.functionName === "callWithSignature" ) { return _writeContract(writeArgs); } + if (writeArgs.functionName === "batchCall") { + if (!writeArgs.args || !Array.isArray(writeArgs.args) || writeArgs.args.length === 0) { + throw new Error("batchCall args should be an array with at least one element"); + } + + const systemCallFromData = writeArgs.args[0].map((systemCallData: Hex[]) => { + return [params.delegatorAddress, ...systemCallData]; + }); + + // Construct args for `batchCallFrom`. + const batchCallFromArgs: typeof writeArgs = { + ...writeArgs, + functionName: "batchCallFrom", + args: [systemCallFromData], + }; + + // Call `writeContract` with the new args. + return getAction(client, writeContract, "writeContract")(batchCallFromArgs); + } + // Encode the World's calldata (which includes the World's function selector). const worldCalldata = encodeFunctionData({ abi: writeArgs.abi, From 3dfd0b8f9246629f7588cd5503bfdd0296e736b0 Mon Sep 17 00:00:00 2001 From: Dhvani Patel Date: Thu, 4 Jul 2024 12:06:32 +0100 Subject: [PATCH 3/8] fix: export types --- packages/common/src/getBurnerPrivateKey.ts | 6 ++++-- packages/store-sync/src/recs/index.ts | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/common/src/getBurnerPrivateKey.ts b/packages/common/src/getBurnerPrivateKey.ts index c11aebb49d..1a08e0c2a2 100644 --- a/packages/common/src/getBurnerPrivateKey.ts +++ b/packages/common/src/getBurnerPrivateKey.ts @@ -1,7 +1,7 @@ import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"; import { isHex, Hex } from "viem"; -function assertPrivateKey(privateKey: string, cacheKey: string): asserts privateKey is Hex { +export function assertPrivateKey(privateKey: string, cacheKey: string): asserts privateKey is Hex { if (!isHex(privateKey)) { console.error("Private key found in cache is not valid hex", { privateKey, cacheKey }); throw new Error(`Private key found in cache (${cacheKey}) is not valid hex`); @@ -11,7 +11,9 @@ function assertPrivateKey(privateKey: string, cacheKey: string): asserts private privateKeyToAccount(privateKey); } -export function getBurnerPrivateKey(cacheKey = "mud:burnerWallet"): Hex { +export const DEFAULT_BURNER_CACHE_KEY = "mud:burnerWallet"; + +export function getBurnerPrivateKey(cacheKey = DEFAULT_BURNER_CACHE_KEY): Hex { const cachedPrivateKey = localStorage.getItem(cacheKey); if (cachedPrivateKey != null) { diff --git a/packages/store-sync/src/recs/index.ts b/packages/store-sync/src/recs/index.ts index d7df9e00b3..2181fc9eb2 100644 --- a/packages/store-sync/src/recs/index.ts +++ b/packages/store-sync/src/recs/index.ts @@ -7,3 +7,4 @@ export * from "./isStoreComponent"; export * from "./recsStorage"; export * from "./singletonEntity"; export * from "./syncToRecs"; +export * from "./getTableEntity"; From f24b65855ebf26d94f872c9591b25ee96d3338ca Mon Sep 17 00:00:00 2001 From: Dhvani Patel Date: Thu, 4 Jul 2024 13:56:47 +0100 Subject: [PATCH 4/8] feat: compile to cjs --- docs/package.json | 1 - e2e/packages/client-vanilla/package.json | 1 - e2e/packages/sync-test/package.json | 4 +- .../sync-test/setup/deployContracts.ts | 2 +- e2e/packages/sync-test/setup/startIndexer.ts | 2 +- e2e/packages/test-data/generateLogs.ts | 2 +- e2e/packages/test-data/package.json | 3 +- examples/faucet-client/package.json | 1 - examples/indexer-client/package.json | 1 - .../packages/client-phaser/package.json | 1 - .../packages/client-react/package.json | 1 - .../packages/client-vanilla/package.json | 1 - .../packages/client/package.json | 1 - package.json | 6 +- packages/abi-ts/package.json | 5 +- packages/abi-ts/tsup.config.ts | 2 +- packages/account-kit/package.json | 7 +- packages/account-kit/tsup.config.ts | 2 +- packages/block-logs-stream/package.json | 1 - packages/block-logs-stream/tsup.config.ts | 2 +- packages/cli/package.json | 11 +- packages/cli/scripts/generate-test-tables.ts | 6 +- packages/cli/src/build.ts | 2 +- packages/cli/src/commands/devnode.ts | 2 +- packages/cli/src/utils/getContractArtifact.ts | 2 +- packages/cli/src/verify.ts | 2 +- packages/cli/tsup.config.ts | 2 +- packages/common/package.json | 7 +- packages/common/src/createNonceManager.ts | 9 +- packages/common/src/foundry/index.ts | 2 +- packages/common/tsup.config.ts | 2 +- packages/config/package.json | 3 +- .../config/src/deprecated/node/loadConfig.ts | 2 +- packages/config/tsup.config.ts | 2 +- packages/dev-tools/package.json | 1 - packages/dev-tools/tsup.config.ts | 2 +- packages/faucet/bin/faucet-server.ts | 74 ++--- packages/faucet/package.json | 1 - packages/faucet/tsup.config.ts | 2 +- packages/gas-report/package.json | 7 +- packages/gas-report/ts/index.ts | 2 +- packages/gas-report/tsup.config.ts | 2 +- packages/protocol-parser/package.json | 1 - packages/protocol-parser/tsup.config.ts | 2 +- packages/query/package.json | 1 - packages/query/tsup.config.ts | 2 +- packages/react/package.json | 1 - packages/react/tsup.config.ts | 2 +- packages/recs/package.json | 1 - packages/recs/tsup.config.ts | 2 +- packages/schema-type/package.json | 1 - packages/schema-type/tsup.config.ts | 2 +- .../bin/postgres-decoded-indexer.ts | 188 ++++++------- .../store-indexer/bin/postgres-indexer.ts | 240 ++++++++-------- packages/store-indexer/bin/sqlite-indexer.ts | 260 +++++++++--------- packages/store-indexer/package.json | 1 - packages/store-indexer/tsup.config.ts | 2 +- packages/store-sync/package.json | 1 - packages/store-sync/src/createStoreSync.ts | 5 +- packages/store-sync/test/globalSetup.ts | 2 +- packages/store-sync/test/mockGame.ts | 2 +- packages/store-sync/tsup.config.ts | 2 +- packages/store/package.json | 3 +- packages/store/ts/config/v2/input.ts | 3 +- packages/store/ts/config/v2/output.ts | 11 +- packages/store/ts/config/v2/schema.ts | 6 +- packages/store/ts/config/v2/scope.ts | 6 +- packages/store/ts/config/v2/store.ts | 6 +- packages/store/ts/config/v2/table.ts | 6 +- packages/store/ts/config/v2/tables.ts | 6 +- .../store/ts/scripts/generate-test-tables.ts | 6 +- .../store/ts/scripts/generate-tightcoder.ts | 16 +- packages/store/ts/scripts/tablegen.ts | 18 +- packages/store/tsup.config.ts | 2 +- packages/utils/package.json | 1 - packages/utils/tsup.config.ts | 2 +- packages/world-modules/package.json | 1 - packages/world-modules/ts/scripts/tablegen.ts | 18 +- packages/world-modules/ts/scripts/worldgen.ts | 42 +-- packages/world-modules/tsup.config.ts | 2 +- packages/world/package.json | 3 +- packages/world/ts/config/v2/input.ts | 41 ++- packages/world/ts/config/v2/world.ts | 36 ++- .../world/ts/scripts/generate-test-tables.ts | 6 +- packages/world/ts/scripts/tablegen.ts | 18 +- packages/world/ts/scripts/worldgen.ts | 46 ++-- packages/world/tsup.config.ts | 2 +- pnpm-lock.yaml | 198 +++++-------- scripts/changelog.ts | 2 +- scripts/render-api-docs.ts | 2 +- templates/phaser/packages/client/package.json | 1 - .../react-ecs/packages/client/package.json | 1 - templates/react/packages/client/package.json | 1 - .../threejs/packages/client/package.json | 1 - .../vanilla/packages/client/package.json | 1 - 95 files changed, 677 insertions(+), 746 deletions(-) diff --git a/docs/package.json b/docs/package.json index a1b0a4430a..b5c5f64d71 100644 --- a/docs/package.json +++ b/docs/package.json @@ -4,7 +4,6 @@ "private": true, "description": "mud.dev docs", "license": "", - "type": "module", "main": "index.js", "scripts": { "build": "next build", diff --git a/e2e/packages/client-vanilla/package.json b/e2e/packages/client-vanilla/package.json index 46d9f8caf9..12bde50e96 100644 --- a/e2e/packages/client-vanilla/package.json +++ b/e2e/packages/client-vanilla/package.json @@ -3,7 +3,6 @@ "version": "0.0.0", "private": true, "license": "MIT", - "type": "module", "scripts": { "build": "vite build", "clean": "rimraf dist", diff --git a/e2e/packages/sync-test/package.json b/e2e/packages/sync-test/package.json index 5b5b09cb0d..fbfb8c963d 100644 --- a/e2e/packages/sync-test/package.json +++ b/e2e/packages/sync-test/package.json @@ -23,10 +23,10 @@ "@latticexyz/world-modules": "link:../../../packages/world-modules", "@viem/anvil": "^0.0.6", "abitype": "1.0.0", - "chalk": "^5.2.0", + "chalk": "4.1.2", "dotenv": "^16.0.3", "drizzle-orm": "^0.28.5", - "execa": "^7.1.1", + "execa": "^5.1.1", "happy-dom": "^12.10.3", "postgres": "3.3.5", "typescript": "5.4.2", diff --git a/e2e/packages/sync-test/setup/deployContracts.ts b/e2e/packages/sync-test/setup/deployContracts.ts index 929dfb1d61..688cdc4fc6 100644 --- a/e2e/packages/sync-test/setup/deployContracts.ts +++ b/e2e/packages/sync-test/setup/deployContracts.ts @@ -1,5 +1,5 @@ import chalk from "chalk"; -import { execa } from "execa"; +import execa from "execa"; export function deployContracts(rpc: string) { const deploymentProcess = execa("pnpm", ["mud", "deploy", "--rpc", rpc], { diff --git a/e2e/packages/sync-test/setup/startIndexer.ts b/e2e/packages/sync-test/setup/startIndexer.ts index ac0d3f9a55..b8954b9ec2 100644 --- a/e2e/packages/sync-test/setup/startIndexer.ts +++ b/e2e/packages/sync-test/setup/startIndexer.ts @@ -1,5 +1,5 @@ import chalk from "chalk"; -import { execa } from "execa"; +import execa from "execa"; import { rmSync } from "node:fs"; import { cleanDatabase } from "@latticexyz/store-sync/postgres"; import { drizzle } from "drizzle-orm/postgres-js"; diff --git a/e2e/packages/test-data/generateLogs.ts b/e2e/packages/test-data/generateLogs.ts index c884066e17..dbdf2673b6 100644 --- a/e2e/packages/test-data/generateLogs.ts +++ b/e2e/packages/test-data/generateLogs.ts @@ -1,4 +1,4 @@ -import { execa } from "execa"; +import execa from "execa"; import { ClientConfig, GetContractReturnType, diff --git a/e2e/packages/test-data/package.json b/e2e/packages/test-data/package.json index bf0d6d3bb9..e39c4353f3 100644 --- a/e2e/packages/test-data/package.json +++ b/e2e/packages/test-data/package.json @@ -3,7 +3,6 @@ "version": "0.0.0", "private": true, "license": "MIT", - "type": "module", "scripts": { "generate-bench-data-bulk": "tsx generate-bench-data-bulk.ts", "generate-bench-data-query": "tsx generate-bench-data-query.ts", @@ -15,7 +14,7 @@ "@latticexyz/common": "link:../../../packages/common", "@latticexyz/store": "link:../../../packages/store", "@viem/anvil": "^0.0.6", - "execa": "^7.1.1", + "execa": "^5.1.1", "tsx": "^3.12.6", "typescript": "5.4.2", "viem": "2.9.20" diff --git a/examples/faucet-client/package.json b/examples/faucet-client/package.json index e64f808787..47dcf058a4 100644 --- a/examples/faucet-client/package.json +++ b/examples/faucet-client/package.json @@ -3,7 +3,6 @@ "version": "0.0.0", "private": true, "license": "MIT", - "type": "module", "scripts": { "drip": "tsx index.ts" }, diff --git a/examples/indexer-client/package.json b/examples/indexer-client/package.json index 3ad7366071..4c592cfac0 100644 --- a/examples/indexer-client/package.json +++ b/examples/indexer-client/package.json @@ -3,7 +3,6 @@ "version": "0.0.0", "private": true, "license": "MIT", - "type": "module", "scripts": { "read-indexer": "tsx index.ts" }, diff --git a/examples/minimal/packages/client-phaser/package.json b/examples/minimal/packages/client-phaser/package.json index 4dca84cfce..cfafcc4333 100644 --- a/examples/minimal/packages/client-phaser/package.json +++ b/examples/minimal/packages/client-phaser/package.json @@ -3,7 +3,6 @@ "version": "0.0.0", "private": true, "license": "MIT", - "type": "module", "scripts": { "build": "vite build", "dev": "vite", diff --git a/examples/minimal/packages/client-react/package.json b/examples/minimal/packages/client-react/package.json index 5c7dd231ce..9604df6137 100644 --- a/examples/minimal/packages/client-react/package.json +++ b/examples/minimal/packages/client-react/package.json @@ -3,7 +3,6 @@ "version": "0.0.0", "private": true, "license": "MIT", - "type": "module", "scripts": { "build": "vite build", "dev": "wait-port localhost:8545 && vite", diff --git a/examples/minimal/packages/client-vanilla/package.json b/examples/minimal/packages/client-vanilla/package.json index 74d4d7680c..1d7a5b8aea 100644 --- a/examples/minimal/packages/client-vanilla/package.json +++ b/examples/minimal/packages/client-vanilla/package.json @@ -3,7 +3,6 @@ "version": "0.0.0", "private": true, "license": "MIT", - "type": "module", "scripts": { "build": "vite build", "dev": "wait-port localhost:8545 && vite", diff --git a/examples/multiple-accounts/packages/client/package.json b/examples/multiple-accounts/packages/client/package.json index a3e628e6be..15bea11881 100644 --- a/examples/multiple-accounts/packages/client/package.json +++ b/examples/multiple-accounts/packages/client/package.json @@ -3,7 +3,6 @@ "version": "0.0.0", "private": true, "license": "MIT", - "type": "module", "scripts": { "build": "vite build", "dev": "wait-port localhost:8545 && vite", diff --git a/package.json b/package.json index b6350d9599..7a64b0ccea 100644 --- a/package.json +++ b/package.json @@ -38,15 +38,15 @@ }, "devDependencies": { "@arktype/attest": "0.7.5", - "@arktype/util": "0.0.43", + "@arktype/util": "0.0.41-cjs", "@changesets/cli": "^2.26.1", "@types/node": "^18.15.11", "@typescript-eslint/eslint-plugin": "7.1.1", "@typescript-eslint/parser": "7.1.1", "bun": "^1.0.11", - "chalk": "^5.2.0", + "chalk": "4.1.2", "eslint": "8.57.0", - "execa": "^7.0.0", + "execa": "^5.1.1", "glob": "^10.4.2", "husky": ">=6", "lint-staged": ">=10", diff --git a/packages/abi-ts/package.json b/packages/abi-ts/package.json index 0fb246f6d8..cfac660ab6 100644 --- a/packages/abi-ts/package.json +++ b/packages/abi-ts/package.json @@ -8,7 +8,6 @@ "directory": "packages/abi-ts" }, "license": "MIT", - "type": "module", "exports": { ".": "./dist/index.js" }, @@ -34,9 +33,9 @@ "test:ci": "pnpm run test" }, "dependencies": { - "chalk": "^5.3.0", + "chalk": "4.1.2", "debug": "^4.3.4", - "execa": "^7.0.0", + "execa": "^5.1.1", "glob": "^10.4.2", "yargs": "^17.7.1" }, diff --git a/packages/abi-ts/tsup.config.ts b/packages/abi-ts/tsup.config.ts index 17e4b13a46..c0b194943c 100644 --- a/packages/abi-ts/tsup.config.ts +++ b/packages/abi-ts/tsup.config.ts @@ -3,7 +3,7 @@ import { defineConfig } from "tsup"; export default defineConfig({ entry: ["src/index.ts", "src/abi-ts.ts"], target: "esnext", - format: ["esm"], + format: ["esm", "cjs"], dts: !process.env.TSUP_SKIP_DTS, sourcemap: true, clean: true, diff --git a/packages/account-kit/package.json b/packages/account-kit/package.json index c5f8ead504..6a7cb022cc 100644 --- a/packages/account-kit/package.json +++ b/packages/account-kit/package.json @@ -8,7 +8,6 @@ "directory": "packages/account-kit" }, "license": "MIT", - "type": "module", "exports": { ".": "./dist/tsup/index.js", "./internal": "./dist/tsup/internal.js", @@ -19,7 +18,7 @@ ], "scripts": { "build": "pnpm run build:js", - "build:js": "tsup & vite build", + "build:js": "tsup", "clean": "pnpm run clean:js", "clean:js": "rimraf dist", "dev": "tsup --watch & vite build --watch", @@ -43,8 +42,8 @@ "@tanstack/react-query": "^5.28.8", "abitype": "1.0.0", "debug": "^4.3.4", - "execa": "^7.0.0", - "p-retry": "^5.1.2", + "execa": "^5.1.1", + "p-retry": "4.6.2", "permissionless": "^0.1.17", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/packages/account-kit/tsup.config.ts b/packages/account-kit/tsup.config.ts index c16a4c3ab8..c2c42d7896 100644 --- a/packages/account-kit/tsup.config.ts +++ b/packages/account-kit/tsup.config.ts @@ -7,7 +7,7 @@ export default defineConfig({ internal: "src/exports/internal.ts", }, target: "esnext", - format: ["esm"], + format: ["esm", "cjs"], dts: false, // TODO: figure out how to reenable sourcemap: true, clean: true, diff --git a/packages/block-logs-stream/package.json b/packages/block-logs-stream/package.json index dabf33c771..4b14314553 100644 --- a/packages/block-logs-stream/package.json +++ b/packages/block-logs-stream/package.json @@ -8,7 +8,6 @@ "directory": "packages/block-logs-stream" }, "license": "MIT", - "type": "module", "exports": { ".": "./dist/index.js" }, diff --git a/packages/block-logs-stream/tsup.config.ts b/packages/block-logs-stream/tsup.config.ts index 86fe9a5339..af92ad0d25 100644 --- a/packages/block-logs-stream/tsup.config.ts +++ b/packages/block-logs-stream/tsup.config.ts @@ -3,7 +3,7 @@ import { defineConfig } from "tsup"; export default defineConfig({ entry: ["src/index.ts"], target: "esnext", - format: ["esm"], + format: ["esm", "cjs"], dts: !process.env.TSUP_SKIP_DTS, sourcemap: true, clean: true, diff --git a/packages/cli/package.json b/packages/cli/package.json index d0a6314f8e..17ec622ba3 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -8,7 +8,6 @@ "directory": "packages/cli" }, "license": "MIT", - "type": "module", "exports": { ".": "./dist/index.js" }, @@ -53,17 +52,17 @@ "@latticexyz/world": "workspace:*", "abitype": "1.0.0", "asn1.js": "^5.4.1", - "chalk": "^5.0.1", + "chalk": "4.1.2", "chokidar": "^3.5.3", "debug": "^4.3.4", "dotenv": "^16.0.3", "ethers": "^5.7.2", - "execa": "^7.0.0", - "find-up": "^6.3.0", + "execa": "^5.1.1", + "find-up": "5.0.0", "glob": "^10.4.2", "openurl": "^1.1.1", - "p-queue": "^7.4.1", - "p-retry": "^5.1.2", + "p-queue": "6.6.2", + "p-retry": "4.6.2", "path": "^0.12.7", "rxjs": "7.5.5", "throttle-debounce": "^5.0.0", diff --git a/packages/cli/scripts/generate-test-tables.ts b/packages/cli/scripts/generate-test-tables.ts index 8274f805e9..ec0173375f 100644 --- a/packages/cli/scripts/generate-test-tables.ts +++ b/packages/cli/scripts/generate-test-tables.ts @@ -95,6 +95,8 @@ const config = defineStore({ }, }); -const remappings = await getRemappings(); +(async () => { + const remappings = await getRemappings(); -await tablegen({ configPath, config, remappings }); + await tablegen({ configPath, config, remappings }); +})(); diff --git a/packages/cli/src/build.ts b/packages/cli/src/build.ts index 9b8fda5f1c..8540124224 100644 --- a/packages/cli/src/build.ts +++ b/packages/cli/src/build.ts @@ -4,7 +4,7 @@ import { worldgen } from "@latticexyz/world/node"; import { World as WorldConfig } from "@latticexyz/world"; import { forge, getRemappings } from "@latticexyz/common/foundry"; import { getExistingContracts } from "./utils/getExistingContracts"; -import { execa } from "execa"; +import execa from "execa"; type BuildOptions = { foundryProfile?: string; diff --git a/packages/cli/src/commands/devnode.ts b/packages/cli/src/commands/devnode.ts index 84378c75af..6452010c73 100644 --- a/packages/cli/src/commands/devnode.ts +++ b/packages/cli/src/commands/devnode.ts @@ -2,7 +2,7 @@ import { rmSync } from "fs"; import { homedir } from "os"; import path from "path"; import type { CommandModule } from "yargs"; -import { execa } from "execa"; +import execa from "execa"; type Options = { blocktime: number; diff --git a/packages/cli/src/utils/getContractArtifact.ts b/packages/cli/src/utils/getContractArtifact.ts index 63d4abbd28..bafd9bb8dd 100644 --- a/packages/cli/src/utils/getContractArtifact.ts +++ b/packages/cli/src/utils/getContractArtifact.ts @@ -4,7 +4,7 @@ import { findPlaceholders } from "./findPlaceholders"; import { z } from "zod"; import { Abi as abiSchema } from "abitype/zod"; import { createRequire } from "node:module"; -import { findUp } from "find-up"; +import findUp from "find-up"; export type GetContractArtifactOptions = { /** diff --git a/packages/cli/src/verify.ts b/packages/cli/src/verify.ts index b5acaad720..5d923d1dec 100644 --- a/packages/cli/src/verify.ts +++ b/packages/cli/src/verify.ts @@ -7,7 +7,7 @@ import { getDeployer } from "./deploy/getDeployer"; import { MUDError } from "@latticexyz/common/errors"; import { Module, salt } from "./deploy/common"; import { getStorageAt } from "viem/actions"; -import { execa } from "execa"; +import execa from "execa"; type VerifyOptions = { client: Client; diff --git a/packages/cli/tsup.config.ts b/packages/cli/tsup.config.ts index 3c4776d5a3..1e5aa07b41 100644 --- a/packages/cli/tsup.config.ts +++ b/packages/cli/tsup.config.ts @@ -20,7 +20,7 @@ const mudPackages: MudPackages = Object.fromEntries( export default defineConfig({ entry: ["src/index.ts", "src/mud.ts"], target: "esnext", - format: ["esm"], + format: ["esm", "cjs"], dts: !process.env.TSUP_SKIP_DTS, sourcemap: true, clean: true, diff --git a/packages/common/package.json b/packages/common/package.json index d1ff16b73f..105e5498e9 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -8,7 +8,6 @@ "directory": "packages/common" }, "license": "MIT", - "type": "module", "exports": { ".": "./dist/index.js", "./actions": "./dist/actions.js", @@ -69,9 +68,9 @@ "@latticexyz/schema-type": "workspace:*", "@solidity-parser/parser": "^0.16.0", "debug": "^4.3.4", - "execa": "^7.0.0", - "p-queue": "^7.4.1", - "p-retry": "^5.1.2", + "execa": "^5.1.1", + "p-queue": "6.6.2", + "p-retry": "4.6.2", "prettier": "3.2.5", "prettier-plugin-solidity": "1.3.1", "viem": "2.9.20" diff --git a/packages/common/src/createNonceManager.ts b/packages/common/src/createNonceManager.ts index 3c781b9914..7afa57d8ce 100644 --- a/packages/common/src/createNonceManager.ts +++ b/packages/common/src/createNonceManager.ts @@ -74,10 +74,13 @@ export function createNonceManager({ debug("reset nonce to", nonceRef.current); } - function shouldResetNonce(error: unknown): boolean { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + function shouldResetNonce(error: any): boolean { return ( - error instanceof BaseError && - error.walk((e) => e instanceof NonceTooLowError || e instanceof NonceTooHighError) != null + (error instanceof BaseError && + error.walk((e) => e instanceof NonceTooLowError || e instanceof NonceTooHighError) != null) || + error.name === "NonceTooLowError" || + error.name === "NonceTooHighError" ); } diff --git a/packages/common/src/foundry/index.ts b/packages/common/src/foundry/index.ts index 628a98deea..c99ffe0e65 100644 --- a/packages/common/src/foundry/index.ts +++ b/packages/common/src/foundry/index.ts @@ -1,4 +1,4 @@ -import { execa, Options } from "execa"; +import execa, { Options } from "execa"; export interface ForgeConfig { // project diff --git a/packages/common/tsup.config.ts b/packages/common/tsup.config.ts index 04177d900b..451f521577 100644 --- a/packages/common/tsup.config.ts +++ b/packages/common/tsup.config.ts @@ -13,7 +13,7 @@ export default defineConfig({ kms: "src/exports/kms.ts", }, target: "esnext", - format: ["esm"], + format: ["esm", "cjs"], dts: !process.env.TSUP_SKIP_DTS, sourcemap: true, clean: true, diff --git a/packages/config/package.json b/packages/config/package.json index 804f98e76d..8835cc1660 100644 --- a/packages/config/package.json +++ b/packages/config/package.json @@ -8,7 +8,6 @@ "directory": "packages/config" }, "license": "MIT", - "type": "module", "exports": { ".": "./dist/index.js", "./internal": "./dist/internal.js", @@ -51,7 +50,7 @@ "@latticexyz/common": "workspace:*", "@latticexyz/schema-type": "workspace:*", "esbuild": "^0.17.15", - "find-up": "^6.3.0", + "find-up": "5.0.0", "viem": "2.9.20", "zod": "^3.22.2", "zod-validation-error": "^1.3.0" diff --git a/packages/config/src/deprecated/node/loadConfig.ts b/packages/config/src/deprecated/node/loadConfig.ts index 9d4a52f338..447b8d7e69 100644 --- a/packages/config/src/deprecated/node/loadConfig.ts +++ b/packages/config/src/deprecated/node/loadConfig.ts @@ -1,4 +1,4 @@ -import { findUp } from "find-up"; +import findUp from "find-up"; import path from "path"; import { NotInsideProjectError } from "../library/errors"; import esbuild from "esbuild"; diff --git a/packages/config/tsup.config.ts b/packages/config/tsup.config.ts index 8204f00f19..b0575ecac2 100644 --- a/packages/config/tsup.config.ts +++ b/packages/config/tsup.config.ts @@ -9,7 +9,7 @@ export default defineConfig({ "deprecated/node": "src/deprecated/node/index.ts", }, target: "esnext", - format: ["esm"], + format: ["esm", "cjs"], dts: !process.env.TSUP_SKIP_DTS, sourcemap: true, clean: true, diff --git a/packages/dev-tools/package.json b/packages/dev-tools/package.json index b22fc7685d..e3d32f1e7b 100644 --- a/packages/dev-tools/package.json +++ b/packages/dev-tools/package.json @@ -8,7 +8,6 @@ "directory": "packages/dev-tools" }, "license": "MIT", - "type": "module", "exports": { ".": "./dist/index.js" }, diff --git a/packages/dev-tools/tsup.config.ts b/packages/dev-tools/tsup.config.ts index 315c0c9fb7..6030b90e79 100644 --- a/packages/dev-tools/tsup.config.ts +++ b/packages/dev-tools/tsup.config.ts @@ -15,7 +15,7 @@ const bundledDeps = Object.keys(packageJson.dependencies).filter((dep) => !peerD export default defineConfig({ entry: ["src/index.ts"], target: "esnext", - format: ["esm"], + format: ["esm", "cjs"], dts: !process.env.TSUP_SKIP_DTS, sourcemap: true, clean: true, diff --git a/packages/faucet/bin/faucet-server.ts b/packages/faucet/bin/faucet-server.ts index dbbd0ac73a..9393928294 100644 --- a/packages/faucet/bin/faucet-server.ts +++ b/packages/faucet/bin/faucet-server.ts @@ -7,39 +7,41 @@ import { privateKeyToAccount } from "viem/accounts"; import { AppRouter, createAppRouter } from "../src/createAppRouter"; import { parseEnv } from "./parseEnv"; -const env = parseEnv(); - -const client = createClient({ - transport: http(env.RPC_HTTP_URL), -}); - -const faucetAccount = privateKeyToAccount(env.FAUCET_PRIVATE_KEY); - -// @see https://fastify.dev/docs/latest/ -const server = fastify({ - maxParamLength: 5000, - logger: true, -}); - -await server.register(import("@fastify/compress")); -await server.register(import("@fastify/cors")); - -// k8s healthchecks -server.get("/healthz", (req, res) => res.code(200).send()); -server.get("/readyz", (req, res) => res.code(200).send()); - -// @see https://trpc.io/docs/server/adapters/fastify -server.register(fastifyTRPCPlugin, { - prefix: "/trpc", - trpcOptions: { - router: createAppRouter(), - createContext: async () => ({ - client, - faucetAccount, - dripAmount: env.DRIP_AMOUNT_ETHER, - }), - }, -}); - -await server.listen({ host: env.HOST, port: env.PORT }); -console.log(`faucet server listening on http://${env.HOST}:${env.PORT}`); +(async (): Promise => { + const env = parseEnv(); + + const client = createClient({ + transport: http(env.RPC_HTTP_URL), + }); + + const faucetAccount = privateKeyToAccount(env.FAUCET_PRIVATE_KEY); + + // @see https://fastify.dev/docs/latest/ + const server = fastify({ + maxParamLength: 5000, + logger: true, + }); + + await server.register(import("@fastify/compress")); + await server.register(import("@fastify/cors")); + + // k8s healthchecks + server.get("/healthz", (req, res) => res.code(200).send()); + server.get("/readyz", (req, res) => res.code(200).send()); + + // @see https://trpc.io/docs/server/adapters/fastify + server.register(fastifyTRPCPlugin, { + prefix: "/trpc", + trpcOptions: { + router: createAppRouter(), + createContext: async () => ({ + client, + faucetAccount, + dripAmount: env.DRIP_AMOUNT_ETHER, + }), + }, + }); + + await server.listen({ host: env.HOST, port: env.PORT }); + console.log(`faucet server listening on http://${env.HOST}:${env.PORT}`); +})(); diff --git a/packages/faucet/package.json b/packages/faucet/package.json index df157cda6f..b8d3d6eaf8 100644 --- a/packages/faucet/package.json +++ b/packages/faucet/package.json @@ -8,7 +8,6 @@ "directory": "packages/faucet" }, "license": "MIT", - "type": "module", "exports": { ".": "./dist/src/index.js" }, diff --git a/packages/faucet/tsup.config.ts b/packages/faucet/tsup.config.ts index 9fd1990d5f..47a75c433f 100644 --- a/packages/faucet/tsup.config.ts +++ b/packages/faucet/tsup.config.ts @@ -3,7 +3,7 @@ import { defineConfig } from "tsup"; export default defineConfig({ entry: ["src/index.ts", "bin/faucet-server.ts"], target: "esnext", - format: ["esm"], + format: ["esm", "cjs"], dts: !process.env.TSUP_SKIP_DTS, sourcemap: true, clean: true, diff --git a/packages/gas-report/package.json b/packages/gas-report/package.json index 092246080c..abd9bb76e3 100644 --- a/packages/gas-report/package.json +++ b/packages/gas-report/package.json @@ -8,7 +8,6 @@ "directory": "packages/gas-report" }, "license": "MIT", - "type": "module", "exports": { ".": "./dist/index.js" }, @@ -36,11 +35,11 @@ "test:ci": "pnpm run test" }, "dependencies": { - "chalk": "^5.3.0", + "chalk": "4.1.2", "dotenv": "^16.0.3", - "execa": "^7.0.0", + "execa": "^5.1.1", "stream-to-array": "^2.3.0", - "strip-ansi": "^7.1.0", + "strip-ansi": "6.0.1", "table": "^6.8.1", "yargs": "^17.7.1" }, diff --git a/packages/gas-report/ts/index.ts b/packages/gas-report/ts/index.ts index e8d41f731b..36ce4480bb 100644 --- a/packages/gas-report/ts/index.ts +++ b/packages/gas-report/ts/index.ts @@ -1,6 +1,6 @@ import type { CommandModule } from "yargs"; import { readFileSync, writeFileSync } from "fs"; -import { execa } from "execa"; +import execa from "execa"; import chalk from "chalk"; import { table, getBorderCharacters } from "table"; import stripAnsi from "strip-ansi"; diff --git a/packages/gas-report/tsup.config.ts b/packages/gas-report/tsup.config.ts index 9249f040b1..4a1ce585e5 100644 --- a/packages/gas-report/tsup.config.ts +++ b/packages/gas-report/tsup.config.ts @@ -3,7 +3,7 @@ import { defineConfig } from "tsup"; export default defineConfig({ entry: ["ts/index.ts", "ts/gas-report.ts"], target: "esnext", - format: ["esm"], + format: ["esm", "cjs"], dts: !process.env.TSUP_SKIP_DTS, sourcemap: true, clean: true, diff --git a/packages/protocol-parser/package.json b/packages/protocol-parser/package.json index 92623041db..670231ab8d 100644 --- a/packages/protocol-parser/package.json +++ b/packages/protocol-parser/package.json @@ -8,7 +8,6 @@ "directory": "packages/protocol-parser" }, "license": "MIT", - "type": "module", "exports": { ".": "./dist/index.js", "./internal": "./dist/internal.js" diff --git a/packages/protocol-parser/tsup.config.ts b/packages/protocol-parser/tsup.config.ts index 709f21cd88..94ab8beb28 100644 --- a/packages/protocol-parser/tsup.config.ts +++ b/packages/protocol-parser/tsup.config.ts @@ -6,7 +6,7 @@ export default defineConfig({ internal: "src/exports/internal.ts", }, target: "esnext", - format: ["esm"], + format: ["esm", "cjs"], dts: !process.env.TSUP_SKIP_DTS, sourcemap: true, clean: true, diff --git a/packages/query/package.json b/packages/query/package.json index 0593440c54..aa1cfa0e9e 100644 --- a/packages/query/package.json +++ b/packages/query/package.json @@ -8,7 +8,6 @@ "directory": "packages/query" }, "license": "MIT", - "type": "module", "exports": { ".": "./dist/index.js", "./internal": "./dist/internal.js" diff --git a/packages/query/tsup.config.ts b/packages/query/tsup.config.ts index 709f21cd88..94ab8beb28 100644 --- a/packages/query/tsup.config.ts +++ b/packages/query/tsup.config.ts @@ -6,7 +6,7 @@ export default defineConfig({ internal: "src/exports/internal.ts", }, target: "esnext", - format: ["esm"], + format: ["esm", "cjs"], dts: !process.env.TSUP_SKIP_DTS, sourcemap: true, clean: true, diff --git a/packages/react/package.json b/packages/react/package.json index 78b151fa58..8244c7a9d1 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -8,7 +8,6 @@ "directory": "packages/react" }, "license": "MIT", - "type": "module", "exports": { ".": "./dist/index.js" }, diff --git a/packages/react/tsup.config.ts b/packages/react/tsup.config.ts index 86fe9a5339..af92ad0d25 100644 --- a/packages/react/tsup.config.ts +++ b/packages/react/tsup.config.ts @@ -3,7 +3,7 @@ import { defineConfig } from "tsup"; export default defineConfig({ entry: ["src/index.ts"], target: "esnext", - format: ["esm"], + format: ["esm", "cjs"], dts: !process.env.TSUP_SKIP_DTS, sourcemap: true, clean: true, diff --git a/packages/recs/package.json b/packages/recs/package.json index a403fdde8a..2a39014eca 100644 --- a/packages/recs/package.json +++ b/packages/recs/package.json @@ -7,7 +7,6 @@ "directory": "packages/recs" }, "license": "MIT", - "type": "module", "exports": { ".": "./dist/index.js", "./deprecated": "./dist/deprecated/index.js" diff --git a/packages/recs/tsup.config.ts b/packages/recs/tsup.config.ts index 7bd5468d6d..0344fc975f 100644 --- a/packages/recs/tsup.config.ts +++ b/packages/recs/tsup.config.ts @@ -3,7 +3,7 @@ import { defineConfig } from "tsup"; export default defineConfig({ entry: ["src/index.ts", "src/deprecated/index.ts"], target: "esnext", - format: ["esm"], + format: ["esm", "cjs"], dts: !process.env.TSUP_SKIP_DTS, sourcemap: true, clean: true, diff --git a/packages/schema-type/package.json b/packages/schema-type/package.json index e4b623616a..c56cd9e2d7 100644 --- a/packages/schema-type/package.json +++ b/packages/schema-type/package.json @@ -8,7 +8,6 @@ "directory": "packages/schema-type" }, "license": "MIT", - "type": "module", "exports": { ".": "./dist/index.js", "./internal": "./dist/internal.js", diff --git a/packages/schema-type/tsup.config.ts b/packages/schema-type/tsup.config.ts index 785c99b7cc..2d871c1d92 100644 --- a/packages/schema-type/tsup.config.ts +++ b/packages/schema-type/tsup.config.ts @@ -8,7 +8,7 @@ export default defineConfig({ }, outDir: "dist", target: "esnext", - format: ["esm"], + format: ["esm", "cjs"], dts: !process.env.TSUP_SKIP_DTS, sourcemap: true, clean: true, diff --git a/packages/store-indexer/bin/postgres-decoded-indexer.ts b/packages/store-indexer/bin/postgres-decoded-indexer.ts index 70bce8a446..64f2321eae 100644 --- a/packages/store-indexer/bin/postgres-decoded-indexer.ts +++ b/packages/store-indexer/bin/postgres-decoded-indexer.ts @@ -14,103 +14,105 @@ import { sentry } from "../src/koa-middleware/sentry"; import { healthcheck } from "../src/koa-middleware/healthcheck"; import { helloWorld } from "../src/koa-middleware/helloWorld"; -const env = parseEnv( - z.intersection( - indexerEnvSchema, - z.object({ - DATABASE_URL: z.string(), - HEALTHCHECK_HOST: z.string().optional(), - HEALTHCHECK_PORT: z.coerce.number().optional(), - SENTRY_DSN: z.string().optional(), - }), - ), -); - -const transports: Transport[] = [ - // prefer WS when specified - env.RPC_WS_URL ? webSocket(env.RPC_WS_URL) : undefined, - // otherwise use or fallback to HTTP - env.RPC_HTTP_URL ? http(env.RPC_HTTP_URL) : undefined, -].filter(isDefined); - -const publicClient = createPublicClient({ - transport: fallback(transports), - pollingInterval: env.POLLING_INTERVAL, -}); - -const chainId = await publicClient.getChainId(); -const database = drizzle(postgres(env.DATABASE_URL, { prepare: false })); - -const { storageAdapter, tables } = await createStorageAdapter({ database, publicClient }); - -let startBlock = env.START_BLOCK; - -// Resume from latest block stored in DB. This will throw if the DB doesn't exist yet, so we wrap in a try/catch and ignore the error. -// TODO: query if the DB exists instead of try/catch -try { - const chainState = await database - .select() - .from(tables.configTable) - .where(eq(tables.configTable.chainId, chainId)) - .limit(1) - .execute() - // Get the first record in a way that returns a possible `undefined` - // TODO: move this to `.findFirst` after upgrading drizzle or `rows[0]` after enabling `noUncheckedIndexedAccess: true` - .then((rows) => rows.find(() => true)); - - if (chainState?.blockNumber != null) { - startBlock = chainState.blockNumber + 1n; - console.log("resuming from block number", startBlock); - } -} catch (error) { - // ignore errors for now -} - -const { latestBlockNumber$, storedBlockLogs$ } = await createStoreSync({ - storageAdapter, - publicClient, - followBlockTag: env.FOLLOW_BLOCK_TAG, - startBlock, - maxBlockRange: env.MAX_BLOCK_RANGE, - address: env.STORE_ADDRESS, -}); - -storedBlockLogs$.subscribe(); - -let isCaughtUp = false; -combineLatest([latestBlockNumber$, storedBlockLogs$]) - .pipe( - filter( - ([latestBlockNumber, { blockNumber: lastBlockNumberProcessed }]) => - latestBlockNumber === lastBlockNumberProcessed, +(async (): Promise => { + const env = parseEnv( + z.intersection( + indexerEnvSchema, + z.object({ + DATABASE_URL: z.string(), + HEALTHCHECK_HOST: z.string().optional(), + HEALTHCHECK_PORT: z.coerce.number().optional(), + SENTRY_DSN: z.string().optional(), + }), ), - first(), - ) - .subscribe(() => { - isCaughtUp = true; - console.log("all caught up"); - }); + ); -if (env.HEALTHCHECK_HOST != null || env.HEALTHCHECK_PORT != null) { - const { default: Koa } = await import("koa"); - const { default: cors } = await import("@koa/cors"); + const transports: Transport[] = [ + // prefer WS when specified + env.RPC_WS_URL ? webSocket(env.RPC_WS_URL) : undefined, + // otherwise use or fallback to HTTP + env.RPC_HTTP_URL ? http(env.RPC_HTTP_URL) : undefined, + ].filter(isDefined); - const server = new Koa(); + const publicClient = createPublicClient({ + transport: fallback(transports), + pollingInterval: env.POLLING_INTERVAL, + }); - if (env.SENTRY_DSN) { - server.use(sentry(env.SENTRY_DSN)); + const chainId = await publicClient.getChainId(); + const database = drizzle(postgres(env.DATABASE_URL, { prepare: false })); + + const { storageAdapter, tables } = await createStorageAdapter({ database, publicClient }); + + let startBlock = env.START_BLOCK; + + // Resume from latest block stored in DB. This will throw if the DB doesn't exist yet, so we wrap in a try/catch and ignore the error. + // TODO: query if the DB exists instead of try/catch + try { + const chainState = await database + .select() + .from(tables.configTable) + .where(eq(tables.configTable.chainId, chainId)) + .limit(1) + .execute() + // Get the first record in a way that returns a possible `undefined` + // TODO: move this to `.findFirst` after upgrading drizzle or `rows[0]` after enabling `noUncheckedIndexedAccess: true` + .then((rows) => rows.find(() => true)); + + if (chainState?.blockNumber != null) { + startBlock = chainState.blockNumber + 1n; + console.log("resuming from block number", startBlock); + } + } catch (error) { + // ignore errors for now } - server.use(cors()); - server.use( - healthcheck({ - isReady: () => isCaughtUp, - }), - ); - server.use(helloWorld()); + const { latestBlockNumber$, storedBlockLogs$ } = await createStoreSync({ + storageAdapter, + publicClient, + followBlockTag: env.FOLLOW_BLOCK_TAG, + startBlock, + maxBlockRange: env.MAX_BLOCK_RANGE, + address: env.STORE_ADDRESS, + }); - server.listen({ host: env.HEALTHCHECK_HOST, port: env.HEALTHCHECK_PORT }); - console.log( - `postgres indexer healthcheck server listening on http://${env.HEALTHCHECK_HOST}:${env.HEALTHCHECK_PORT}`, - ); -} + storedBlockLogs$.subscribe(); + + let isCaughtUp = false; + combineLatest([latestBlockNumber$, storedBlockLogs$]) + .pipe( + filter( + ([latestBlockNumber, { blockNumber: lastBlockNumberProcessed }]) => + latestBlockNumber === lastBlockNumberProcessed, + ), + first(), + ) + .subscribe(() => { + isCaughtUp = true; + console.log("all caught up"); + }); + + if (env.HEALTHCHECK_HOST != null || env.HEALTHCHECK_PORT != null) { + const { default: Koa } = await import("koa"); + const { default: cors } = await import("@koa/cors"); + + const server = new Koa(); + + if (env.SENTRY_DSN) { + server.use(sentry(env.SENTRY_DSN)); + } + + server.use(cors()); + server.use( + healthcheck({ + isReady: () => isCaughtUp, + }), + ); + server.use(helloWorld()); + + server.listen({ host: env.HEALTHCHECK_HOST, port: env.HEALTHCHECK_PORT }); + console.log( + `postgres indexer healthcheck server listening on http://${env.HEALTHCHECK_HOST}:${env.HEALTHCHECK_PORT}`, + ); + } +})(); diff --git a/packages/store-indexer/bin/postgres-indexer.ts b/packages/store-indexer/bin/postgres-indexer.ts index 2e92ef3fdd..2988c4aac7 100644 --- a/packages/store-indexer/bin/postgres-indexer.ts +++ b/packages/store-indexer/bin/postgres-indexer.ts @@ -11,128 +11,130 @@ import { cleanDatabase, createStorageAdapter, shouldCleanDatabase } from "@latti import { createStoreSync } from "@latticexyz/store-sync"; import { indexerEnvSchema, parseEnv } from "./parseEnv"; -const env = parseEnv( - z.intersection( - indexerEnvSchema, - z.object({ - DATABASE_URL: z.string(), - HEALTHCHECK_HOST: z.string().optional(), - HEALTHCHECK_PORT: z.coerce.number().optional(), - }), - ), -); - -const transports: Transport[] = [ - // prefer WS when specified - env.RPC_WS_URL ? webSocket(env.RPC_WS_URL) : undefined, - // otherwise use or fallback to HTTP - env.RPC_HTTP_URL ? http(env.RPC_HTTP_URL) : undefined, -].filter(isDefined); - -const publicClient = createPublicClient({ - transport: fallback(transports), - pollingInterval: env.POLLING_INTERVAL, -}); - -const chainId = await publicClient.getChainId(); -const database = drizzle(postgres(env.DATABASE_URL, { prepare: false })); - -if (await shouldCleanDatabase(database, chainId)) { - console.log("outdated database detected, clearing data to start fresh"); - await cleanDatabase(database); -} - -const { storageAdapter, tables } = await createStorageAdapter({ database, publicClient }); - -let startBlock = env.START_BLOCK; - -async function getLatestStoredBlockNumber(): Promise { - // Fetch latest block stored in DB. This will throw if the DB doesn't exist yet, so we wrap in a try/catch and ignore the error. - // TODO: query if the DB exists instead of try/catch - try { - const chainState = await database - .select() - .from(tables.configTable) - .where(eq(tables.configTable.chainId, chainId)) - .limit(1) - .execute() - // Get the first record in a way that returns a possible `undefined` - // TODO: move this to `.findFirst` after upgrading drizzle or `rows[0]` after enabling `noUncheckedIndexedAccess: true` - .then((rows) => rows.find(() => true)); - - return chainState?.blockNumber; - } catch (error) { - // ignore errors for now - } -} - -async function getDistanceFromFollowBlock(): Promise { - const [latestStoredBlockNumber, latestFollowBlock] = await Promise.all([ - getLatestStoredBlockNumber(), - publicClient.getBlock({ blockTag: env.FOLLOW_BLOCK_TAG }), - ]); - return latestFollowBlock.number - (latestStoredBlockNumber ?? -1n); -} - -const latestStoredBlockNumber = await getLatestStoredBlockNumber(); -if (latestStoredBlockNumber != null) { - startBlock = latestStoredBlockNumber + 1n; - console.log("resuming from block number", startBlock); -} - -const { latestBlockNumber$, storedBlockLogs$ } = await createStoreSync({ - storageAdapter, - publicClient, - followBlockTag: env.FOLLOW_BLOCK_TAG, - startBlock, - maxBlockRange: env.MAX_BLOCK_RANGE, - address: env.STORE_ADDRESS, -}); - -storedBlockLogs$.subscribe(); - -let isCaughtUp = false; -combineLatest([latestBlockNumber$, storedBlockLogs$]) - .pipe( - filter( - ([latestBlockNumber, { blockNumber: lastBlockNumberProcessed }]) => - latestBlockNumber === lastBlockNumberProcessed, +(async (): Promise => { + const env = parseEnv( + z.intersection( + indexerEnvSchema, + z.object({ + DATABASE_URL: z.string(), + HEALTHCHECK_HOST: z.string().optional(), + HEALTHCHECK_PORT: z.coerce.number().optional(), + }), ), - first(), - ) - .subscribe(() => { - isCaughtUp = true; - console.log("all caught up"); + ); + + const transports: Transport[] = [ + // prefer WS when specified + env.RPC_WS_URL ? webSocket(env.RPC_WS_URL) : undefined, + // otherwise use or fallback to HTTP + env.RPC_HTTP_URL ? http(env.RPC_HTTP_URL) : undefined, + ].filter(isDefined); + + const publicClient = createPublicClient({ + transport: fallback(transports), + pollingInterval: env.POLLING_INTERVAL, }); -if (env.HEALTHCHECK_HOST != null || env.HEALTHCHECK_PORT != null) { - const { default: Koa } = await import("koa"); - const { default: cors } = await import("@koa/cors"); - const { healthcheck } = await import("../src/koa-middleware/healthcheck"); - const { metrics } = await import("../src/koa-middleware/metrics"); - const { helloWorld } = await import("../src/koa-middleware/helloWorld"); + const chainId = await publicClient.getChainId(); + const database = drizzle(postgres(env.DATABASE_URL, { prepare: false })); - const server = new Koa(); + if (await shouldCleanDatabase(database, chainId)) { + console.log("outdated database detected, clearing data to start fresh"); + await cleanDatabase(database); + } - server.use(cors()); - server.use( - healthcheck({ - isReady: () => isCaughtUp, - }), - ); - server.use( - metrics({ - isHealthy: () => true, - isReady: () => isCaughtUp, - getLatestStoredBlockNumber, - getDistanceFromFollowBlock, - followBlockTag: env.FOLLOW_BLOCK_TAG, - }), - ); - server.use(helloWorld()); + const { storageAdapter, tables } = await createStorageAdapter({ database, publicClient }); + + let startBlock = env.START_BLOCK; + + async function getLatestStoredBlockNumber(): Promise { + // Fetch latest block stored in DB. This will throw if the DB doesn't exist yet, so we wrap in a try/catch and ignore the error. + // TODO: query if the DB exists instead of try/catch + try { + const chainState = await database + .select() + .from(tables.configTable) + .where(eq(tables.configTable.chainId, chainId)) + .limit(1) + .execute() + // Get the first record in a way that returns a possible `undefined` + // TODO: move this to `.findFirst` after upgrading drizzle or `rows[0]` after enabling `noUncheckedIndexedAccess: true` + .then((rows) => rows.find(() => true)); + + return chainState?.blockNumber; + } catch (error) { + // ignore errors for now + } + } - server.listen({ host: env.HEALTHCHECK_HOST, port: env.HEALTHCHECK_PORT }); - console.log( - `postgres indexer healthcheck server listening on http://${env.HEALTHCHECK_HOST}:${env.HEALTHCHECK_PORT}`, - ); -} + async function getDistanceFromFollowBlock(): Promise { + const [latestStoredBlockNumber, latestFollowBlock] = await Promise.all([ + getLatestStoredBlockNumber(), + publicClient.getBlock({ blockTag: env.FOLLOW_BLOCK_TAG }), + ]); + return latestFollowBlock.number - (latestStoredBlockNumber ?? -1n); + } + + const latestStoredBlockNumber = await getLatestStoredBlockNumber(); + if (latestStoredBlockNumber != null) { + startBlock = latestStoredBlockNumber + 1n; + console.log("resuming from block number", startBlock); + } + + const { latestBlockNumber$, storedBlockLogs$ } = await createStoreSync({ + storageAdapter, + publicClient, + followBlockTag: env.FOLLOW_BLOCK_TAG, + startBlock, + maxBlockRange: env.MAX_BLOCK_RANGE, + address: env.STORE_ADDRESS, + }); + + storedBlockLogs$.subscribe(); + + let isCaughtUp = false; + combineLatest([latestBlockNumber$, storedBlockLogs$]) + .pipe( + filter( + ([latestBlockNumber, { blockNumber: lastBlockNumberProcessed }]) => + latestBlockNumber === lastBlockNumberProcessed, + ), + first(), + ) + .subscribe(() => { + isCaughtUp = true; + console.log("all caught up"); + }); + + if (env.HEALTHCHECK_HOST != null || env.HEALTHCHECK_PORT != null) { + const { default: Koa } = await import("koa"); + const { default: cors } = await import("@koa/cors"); + const { healthcheck } = await import("../src/koa-middleware/healthcheck"); + const { metrics } = await import("../src/koa-middleware/metrics"); + const { helloWorld } = await import("../src/koa-middleware/helloWorld"); + + const server = new Koa(); + + server.use(cors()); + server.use( + healthcheck({ + isReady: () => isCaughtUp, + }), + ); + server.use( + metrics({ + isHealthy: () => true, + isReady: () => isCaughtUp, + getLatestStoredBlockNumber, + getDistanceFromFollowBlock, + followBlockTag: env.FOLLOW_BLOCK_TAG, + }), + ); + server.use(helloWorld()); + + server.listen({ host: env.HEALTHCHECK_HOST, port: env.HEALTHCHECK_PORT }); + console.log( + `postgres indexer healthcheck server listening on http://${env.HEALTHCHECK_HOST}:${env.HEALTHCHECK_PORT}`, + ); + } +})(); diff --git a/packages/store-indexer/bin/sqlite-indexer.ts b/packages/store-indexer/bin/sqlite-indexer.ts index e2198cbec6..0ad0217be3 100644 --- a/packages/store-indexer/bin/sqlite-indexer.ts +++ b/packages/store-indexer/bin/sqlite-indexer.ts @@ -21,141 +21,143 @@ import { apiRoutes } from "../src/sqlite/apiRoutes"; import { sentry } from "../src/koa-middleware/sentry"; import { metrics } from "../src/koa-middleware/metrics"; -const env = parseEnv( - z.intersection( - z.intersection(indexerEnvSchema, frontendEnvSchema), - z.object({ - SQLITE_FILENAME: z.string().default("indexer.db"), - SENTRY_DSN: z.string().optional(), - }), - ), -); - -const transports: Transport[] = [ - // prefer WS when specified - env.RPC_WS_URL ? webSocket(env.RPC_WS_URL) : undefined, - // otherwise use or fallback to HTTP - env.RPC_HTTP_URL ? http(env.RPC_HTTP_URL) : undefined, -].filter(isDefined); - -const publicClient = createPublicClient({ - transport: fallback(transports), - pollingInterval: env.POLLING_INTERVAL, -}); - -const chainId = await publicClient.getChainId(); -const database = drizzle(new Database(env.SQLITE_FILENAME)); - -let startBlock = env.START_BLOCK; - -async function getCurrentChainState(): Promise< - | { - schemaVersion: number; - chainId: number; - lastUpdatedBlockNumber: bigint | null; - lastError: string | null; +(async (): Promise => { + const env = parseEnv( + z.intersection( + z.intersection(indexerEnvSchema, frontendEnvSchema), + z.object({ + SQLITE_FILENAME: z.string().default("indexer.db"), + SENTRY_DSN: z.string().optional(), + }), + ), + ); + + const transports: Transport[] = [ + // prefer WS when specified + env.RPC_WS_URL ? webSocket(env.RPC_WS_URL) : undefined, + // otherwise use or fallback to HTTP + env.RPC_HTTP_URL ? http(env.RPC_HTTP_URL) : undefined, + ].filter(isDefined); + + const publicClient = createPublicClient({ + transport: fallback(transports), + pollingInterval: env.POLLING_INTERVAL, + }); + + const chainId = await publicClient.getChainId(); + const database = drizzle(new Database(env.SQLITE_FILENAME)); + + let startBlock = env.START_BLOCK; + + async function getCurrentChainState(): Promise< + | { + schemaVersion: number; + chainId: number; + lastUpdatedBlockNumber: bigint | null; + lastError: string | null; + } + | undefined + > { + // This will throw if the DB doesn't exist yet, so we wrap in a try/catch and ignore the error. + try { + const currentChainStates = database.select().from(chainState).where(eq(chainState.chainId, chainId)).all(); + // TODO: replace this type workaround with `noUncheckedIndexedAccess: true` when we can fix all the issues related (https://github.com/latticexyz/mud/issues/1212) + const currentChainState: (typeof currentChainStates)[number] | undefined = currentChainStates[0]; + return currentChainState; + } catch (error) { + // ignore errors, this is optional } - | undefined -> { - // This will throw if the DB doesn't exist yet, so we wrap in a try/catch and ignore the error. - try { - const currentChainStates = database.select().from(chainState).where(eq(chainState.chainId, chainId)).all(); - // TODO: replace this type workaround with `noUncheckedIndexedAccess: true` when we can fix all the issues related (https://github.com/latticexyz/mud/issues/1212) - const currentChainState: (typeof currentChainStates)[number] | undefined = currentChainStates[0]; - return currentChainState; - } catch (error) { - // ignore errors, this is optional } -} -async function getLatestStoredBlockNumber(): Promise { + async function getLatestStoredBlockNumber(): Promise { + const currentChainState = await getCurrentChainState(); + return currentChainState?.lastUpdatedBlockNumber ?? undefined; + } + + async function getDistanceFromFollowBlock(): Promise { + const [latestStoredBlockNumber, latestFollowBlock] = await Promise.all([ + getLatestStoredBlockNumber(), + publicClient.getBlock({ blockTag: env.FOLLOW_BLOCK_TAG }), + ]); + return latestFollowBlock.number - (latestStoredBlockNumber ?? -1n); + } + const currentChainState = await getCurrentChainState(); - return currentChainState?.lastUpdatedBlockNumber ?? undefined; -} - -async function getDistanceFromFollowBlock(): Promise { - const [latestStoredBlockNumber, latestFollowBlock] = await Promise.all([ - getLatestStoredBlockNumber(), - publicClient.getBlock({ blockTag: env.FOLLOW_BLOCK_TAG }), - ]); - return latestFollowBlock.number - (latestStoredBlockNumber ?? -1n); -} - -const currentChainState = await getCurrentChainState(); -if (currentChainState) { - // Reset the db if the version changed - if (currentChainState.schemaVersion != schemaVersion) { - console.log( - "schema version changed from", - currentChainState.schemaVersion, - "to", - schemaVersion, - "recreating database", - ); - fs.truncateSync(env.SQLITE_FILENAME); - } else if (currentChainState.lastUpdatedBlockNumber != null) { - // Resume from latest block stored in DB. This will throw if the DB doesn't exist yet, so we wrap in a try/catch and ignore the error. - console.log("resuming from block number", currentChainState.lastUpdatedBlockNumber + 1n); - startBlock = currentChainState.lastUpdatedBlockNumber + 1n; + if (currentChainState) { + // Reset the db if the version changed + if (currentChainState.schemaVersion != schemaVersion) { + console.log( + "schema version changed from", + currentChainState.schemaVersion, + "to", + schemaVersion, + "recreating database", + ); + fs.truncateSync(env.SQLITE_FILENAME); + } else if (currentChainState.lastUpdatedBlockNumber != null) { + // Resume from latest block stored in DB. This will throw if the DB doesn't exist yet, so we wrap in a try/catch and ignore the error. + console.log("resuming from block number", currentChainState.lastUpdatedBlockNumber + 1n); + startBlock = currentChainState.lastUpdatedBlockNumber + 1n; + } } -} - -const { latestBlockNumber$, storedBlockLogs$ } = await syncToSqlite({ - database, - publicClient, - followBlockTag: env.FOLLOW_BLOCK_TAG, - startBlock, - maxBlockRange: env.MAX_BLOCK_RANGE, - address: env.STORE_ADDRESS, -}); - -let isCaughtUp = false; -combineLatest([latestBlockNumber$, storedBlockLogs$]) - .pipe( - filter( - ([latestBlockNumber, { blockNumber: lastBlockNumberProcessed }]) => - latestBlockNumber === lastBlockNumberProcessed, - ), - first(), - ) - .subscribe(() => { - isCaughtUp = true; - console.log("all caught up"); - }); -const server = new Koa(); - -if (env.SENTRY_DSN) { - server.use(sentry(env.SENTRY_DSN)); -} - -server.use(cors()); -server.use( - healthcheck({ - isReady: () => isCaughtUp, - }), -); -server.use( - metrics({ - isHealthy: () => true, - isReady: () => isCaughtUp, - getLatestStoredBlockNumber, - getDistanceFromFollowBlock, + const { latestBlockNumber$, storedBlockLogs$ } = await syncToSqlite({ + database, + publicClient, followBlockTag: env.FOLLOW_BLOCK_TAG, - }), -); -server.use(helloWorld()); -server.use(apiRoutes(database)); - -server.use( - createKoaMiddleware({ - prefix: "/trpc", - router: createAppRouter(), - createContext: async () => ({ - queryAdapter: await createQueryAdapter(database), + startBlock, + maxBlockRange: env.MAX_BLOCK_RANGE, + address: env.STORE_ADDRESS, + }); + + let isCaughtUp = false; + combineLatest([latestBlockNumber$, storedBlockLogs$]) + .pipe( + filter( + ([latestBlockNumber, { blockNumber: lastBlockNumberProcessed }]) => + latestBlockNumber === lastBlockNumberProcessed, + ), + first(), + ) + .subscribe(() => { + isCaughtUp = true; + console.log("all caught up"); + }); + + const server = new Koa(); + + if (env.SENTRY_DSN) { + server.use(sentry(env.SENTRY_DSN)); + } + + server.use(cors()); + server.use( + healthcheck({ + isReady: () => isCaughtUp, + }), + ); + server.use( + metrics({ + isHealthy: () => true, + isReady: () => isCaughtUp, + getLatestStoredBlockNumber, + getDistanceFromFollowBlock, + followBlockTag: env.FOLLOW_BLOCK_TAG, + }), + ); + server.use(helloWorld()); + server.use(apiRoutes(database)); + + server.use( + createKoaMiddleware({ + prefix: "/trpc", + router: createAppRouter(), + createContext: async () => ({ + queryAdapter: await createQueryAdapter(database), + }), }), - }), -); + ); -server.listen({ host: env.HOST, port: env.PORT }); -console.log(`sqlite indexer frontend listening on http://${env.HOST}:${env.PORT}`); + server.listen({ host: env.HOST, port: env.PORT }); + console.log(`sqlite indexer frontend listening on http://${env.HOST}:${env.PORT}`); +})(); diff --git a/packages/store-indexer/package.json b/packages/store-indexer/package.json index b685affebc..521d78c44f 100644 --- a/packages/store-indexer/package.json +++ b/packages/store-indexer/package.json @@ -8,7 +8,6 @@ "directory": "packages/store-indexer" }, "license": "MIT", - "type": "module", "exports": { ".": "./dist/index.js" }, diff --git a/packages/store-indexer/tsup.config.ts b/packages/store-indexer/tsup.config.ts index 9b3595745c..001e4d2260 100644 --- a/packages/store-indexer/tsup.config.ts +++ b/packages/store-indexer/tsup.config.ts @@ -9,7 +9,7 @@ export default defineConfig({ "bin/sqlite-indexer.ts", ], target: "esnext", - format: ["esm"], + format: ["esm", "cjs"], dts: !process.env.TSUP_SKIP_DTS, sourcemap: true, clean: true, diff --git a/packages/store-sync/package.json b/packages/store-sync/package.json index 17a41ceb44..50d2d1787d 100644 --- a/packages/store-sync/package.json +++ b/packages/store-sync/package.json @@ -8,7 +8,6 @@ "directory": "packages/store-sync" }, "license": "MIT", - "type": "module", "exports": { ".": "./dist/index.js", "./indexer-client": "./dist/indexer-client/index.js", diff --git a/packages/store-sync/src/createStoreSync.ts b/packages/store-sync/src/createStoreSync.ts index c04b77c9d7..5f75794fd3 100644 --- a/packages/store-sync/src/createStoreSync.ts +++ b/packages/store-sync/src/createStoreSync.ts @@ -304,8 +304,9 @@ export async function createStoreSync( if (lastBlock.blockNumber >= blockNumber) { return { status, blockNumber, transactionHash }; } - } catch (error) { - if (error instanceof TransactionReceiptNotFoundError) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } catch (error: any) { + if (error instanceof TransactionReceiptNotFoundError || error.name === "TransactionReceiptNotFoundError") { return; } throw error; diff --git a/packages/store-sync/test/globalSetup.ts b/packages/store-sync/test/globalSetup.ts index 926984f2eb..70fb710b79 100644 --- a/packages/store-sync/test/globalSetup.ts +++ b/packages/store-sync/test/globalSetup.ts @@ -1,6 +1,6 @@ import { startProxy as startAnvilProxy } from "@viem/anvil"; import { anvilHost, anvilPort } from "./common"; -import { execa } from "execa"; +import execa from "execa"; export default async function globalSetup(): Promise<() => Promise> { console.log("building mock game"); diff --git a/packages/store-sync/test/mockGame.ts b/packages/store-sync/test/mockGame.ts index 29e2ca0fab..14955fa8ce 100644 --- a/packages/store-sync/test/mockGame.ts +++ b/packages/store-sync/test/mockGame.ts @@ -1,4 +1,4 @@ -import { execa } from "execa"; +import execa from "execa"; import { anvilRpcUrl } from "./common"; import configV2 from "mock-game-contracts/mud.config"; import { resolveConfig } from "@latticexyz/store/internal"; diff --git a/packages/store-sync/tsup.config.ts b/packages/store-sync/tsup.config.ts index d8f6860bea..c5d0b3568a 100644 --- a/packages/store-sync/tsup.config.ts +++ b/packages/store-sync/tsup.config.ts @@ -12,7 +12,7 @@ export default defineConfig({ "src/zustand/index.ts", ], target: "esnext", - format: ["esm"], + format: ["esm", "cjs"], dts: !process.env.TSUP_SKIP_DTS, sourcemap: true, clean: true, diff --git a/packages/store/package.json b/packages/store/package.json index a787d9e127..09e58bb3a4 100644 --- a/packages/store/package.json +++ b/packages/store/package.json @@ -8,7 +8,6 @@ "directory": "packages/store" }, "license": "MIT", - "type": "module", "exports": { ".": "./dist/index.js", "./internal": "./dist/internal.js", @@ -67,7 +66,7 @@ "test:ci": "pnpm run test" }, "dependencies": { - "@arktype/util": "0.0.29", + "@arktype/util": "0.0.41-cjs", "@latticexyz/common": "workspace:*", "@latticexyz/config": "workspace:*", "@latticexyz/protocol-parser": "workspace:*", diff --git a/packages/store/ts/config/v2/input.ts b/packages/store/ts/config/v2/input.ts index 60c6b2dbad..71aa0546a4 100644 --- a/packages/store/ts/config/v2/input.ts +++ b/packages/store/ts/config/v2/input.ts @@ -1,7 +1,6 @@ import { Hex } from "viem"; import { Codegen, TableCodegen, TableDeploy, UserTypes } from "./output"; import { Scope } from "./scope"; -import { evaluate } from "@arktype/util"; export type EnumsInput = { readonly [enumName: string]: readonly [string, ...string[]]; @@ -58,4 +57,4 @@ export type TablesWithShorthandsInput = { readonly [key: string]: TableInput | TableShorthandInput; }; -export type StoreWithShorthandsInput = evaluate & { tables: TablesWithShorthandsInput }>; +export type StoreWithShorthandsInput = Omit & { tables: TablesWithShorthandsInput }; diff --git a/packages/store/ts/config/v2/output.ts b/packages/store/ts/config/v2/output.ts index 9db233f7d6..7aa038484c 100644 --- a/packages/store/ts/config/v2/output.ts +++ b/packages/store/ts/config/v2/output.ts @@ -1,4 +1,3 @@ -import { evaluate } from "@arktype/util"; import { AbiType, Schema, Table as BaseTable } from "@latticexyz/config"; import { EnumsInput } from "./input"; @@ -33,12 +32,10 @@ export type TableDeploy = { readonly disabled: boolean; }; -export type Table = evaluate< - BaseTable & { - readonly codegen: TableCodegen; - readonly deploy: TableDeploy; - } ->; +export type Table = BaseTable & { + readonly codegen: TableCodegen; + readonly deploy: TableDeploy; +}; export type Codegen = { /** @internal */ diff --git a/packages/store/ts/config/v2/schema.ts b/packages/store/ts/config/v2/schema.ts index 099d2fd820..daeeb9498d 100644 --- a/packages/store/ts/config/v2/schema.ts +++ b/packages/store/ts/config/v2/schema.ts @@ -1,4 +1,4 @@ -import { conform, evaluate } from "@arktype/util"; +import { conform } from "@arktype/util"; import { AbiTypeScope, Scope } from "./scope"; import { hasOwnKey, isObject } from "./generics"; import { SchemaInput } from "./input"; @@ -27,7 +27,7 @@ export function validateSchema( } } -export type resolveSchema = evaluate<{ +export type resolveSchema = { readonly [key in keyof schema]: { /** the Solidity primitive ABI type */ readonly type: schema[key] extends FixedArrayAbiType @@ -36,7 +36,7 @@ export type resolveSchema = evaluate<{ /** the user defined type or Solidity primitive ABI type */ readonly internalType: schema[key]; }; -}>; +}; export function resolveSchema( schema: schema, diff --git a/packages/store/ts/config/v2/scope.ts b/packages/store/ts/config/v2/scope.ts index a5f70e6f5d..b7c5e99b37 100644 --- a/packages/store/ts/config/v2/scope.ts +++ b/packages/store/ts/config/v2/scope.ts @@ -1,4 +1,4 @@ -import { Dict, evaluate } from "@arktype/util"; +import { Dict } from "@arktype/util"; import { SchemaInput } from "./input"; import { StaticAbiType, schemaAbiTypes } from "@latticexyz/schema-type/internal"; import { AbiType } from "./output"; @@ -24,8 +24,8 @@ export type getStaticAbiTypeKeys< [key in keyof schema]: scope["types"] extends { [_ in schema[key]]: StaticAbiType } ? key : never; }[keyof schema]; -export type extendScope> = evaluate< - ScopeOptions> +export type extendScope> = ScopeOptions< + scope["types"] & additionalTypes >; export function extendScope>( diff --git a/packages/store/ts/config/v2/store.ts b/packages/store/ts/config/v2/store.ts index 1337a38286..570f8a941c 100644 --- a/packages/store/ts/config/v2/store.ts +++ b/packages/store/ts/config/v2/store.ts @@ -1,4 +1,4 @@ -import { ErrorMessage, evaluate, flatMorph, narrow } from "@arktype/util"; +import { ErrorMessage, flatMorph, narrow } from "@arktype/util"; import { get, hasOwnKey, mergeIfUndefined } from "./generics"; import { UserTypes } from "./output"; import { CONFIG_DEFAULTS } from "./defaults"; @@ -59,8 +59,8 @@ export type resolveStore = { > : {}; readonly userTypes: "userTypes" extends keyof store ? store["userTypes"] : {}; - readonly enums: "enums" extends keyof store ? evaluate> : {}; - readonly enumValues: "enums" extends keyof store ? evaluate> : {}; + readonly enums: "enums" extends keyof store ? resolveEnums : {}; + readonly enumValues: "enums" extends keyof store ? mapEnums : {}; readonly namespace: "namespace" extends keyof store ? store["namespace"] : CONFIG_DEFAULTS["namespace"]; readonly codegen: "codegen" extends keyof store ? resolveCodegen : resolveCodegen<{}>; }; diff --git a/packages/store/ts/config/v2/table.ts b/packages/store/ts/config/v2/table.ts index e07ef4863d..5c504c317e 100644 --- a/packages/store/ts/config/v2/table.ts +++ b/packages/store/ts/config/v2/table.ts @@ -1,4 +1,4 @@ -import { ErrorMessage, conform, evaluate, narrow, requiredKeyOf } from "@arktype/util"; +import { ErrorMessage, conform, narrow, requiredKeyOf } from "@arktype/util"; import { isStaticAbiType } from "@latticexyz/schema-type/internal"; import { Hex } from "viem"; import { get, hasOwnKey, mergeIfUndefined } from "./generics"; @@ -101,7 +101,7 @@ export function validateTable( } } -export type resolveTableCodegen = evaluate<{ +export type resolveTableCodegen = { [key in keyof TableCodegen]-?: key extends keyof input["codegen"] ? undefined extends input["codegen"][key] ? key extends "dataStruct" @@ -116,7 +116,7 @@ export type resolveTableCodegen = evaluate<{ : key extends keyof TABLE_CODEGEN_DEFAULTS ? TABLE_CODEGEN_DEFAULTS[key] : never; -}>; +}; export function resolveTableCodegen(input: input): resolveTableCodegen { const options = input.codegen; diff --git a/packages/store/ts/config/v2/tables.ts b/packages/store/ts/config/v2/tables.ts index 3e25809b91..d81a512ee6 100644 --- a/packages/store/ts/config/v2/tables.ts +++ b/packages/store/ts/config/v2/tables.ts @@ -1,4 +1,4 @@ -import { ErrorMessage, evaluate } from "@arktype/util"; +import { ErrorMessage } from "@arktype/util"; import { isObject, mergeIfUndefined } from "./generics"; import { TablesInput } from "./input"; import { Scope, AbiTypeScope } from "./scope"; @@ -23,9 +23,9 @@ export function validateTables( throw new Error(`Expected store config, received ${JSON.stringify(input)}`); } -export type resolveTables = evaluate<{ +export type resolveTables = { readonly [key in keyof tables]: resolveTable, scope>; -}>; +}; export function resolveTables( tables: tables, diff --git a/packages/store/ts/scripts/generate-test-tables.ts b/packages/store/ts/scripts/generate-test-tables.ts index 5d7683d71f..3bd0729fec 100644 --- a/packages/store/ts/scripts/generate-test-tables.ts +++ b/packages/store/ts/scripts/generate-test-tables.ts @@ -51,6 +51,8 @@ const config = defineStore({ }, }); -const remappings = await getRemappings(); +(async () => { + const remappings = await getRemappings(); -await tablegen({ configPath, config, remappings }); + await tablegen({ configPath, config, remappings }); +})(); diff --git a/packages/store/ts/scripts/generate-tightcoder.ts b/packages/store/ts/scripts/generate-tightcoder.ts index d4f0a56579..7b740e51ab 100644 --- a/packages/store/ts/scripts/generate-tightcoder.ts +++ b/packages/store/ts/scripts/generate-tightcoder.ts @@ -1,10 +1,12 @@ import { formatAndWriteSolidity } from "@latticexyz/common/codegen"; import { renderDecodeSlice, renderEncodeArray, renderTightCoderAutoTest } from "../codegen/tightcoder"; -await formatAndWriteSolidity(renderDecodeSlice(), "src/tightcoder/DecodeSlice.sol", "Generated DecodeSlice"); -await formatAndWriteSolidity(renderEncodeArray(), "src/tightcoder/EncodeArray.sol", "Generated EncodeArray"); -await formatAndWriteSolidity( - renderTightCoderAutoTest(), - "test/tightcoder/TightCoderAuto.t.sol", - "Generated TightCoderAutoTest", -); +(async () => { + await formatAndWriteSolidity(renderDecodeSlice(), "src/tightcoder/DecodeSlice.sol", "Generated DecodeSlice"); + await formatAndWriteSolidity(renderEncodeArray(), "src/tightcoder/EncodeArray.sol", "Generated EncodeArray"); + await formatAndWriteSolidity( + renderTightCoderAutoTest(), + "test/tightcoder/TightCoderAuto.t.sol", + "Generated TightCoderAutoTest", + ); +})(); diff --git a/packages/store/ts/scripts/tablegen.ts b/packages/store/ts/scripts/tablegen.ts index 7a255b21f1..58e109e6d8 100644 --- a/packages/store/ts/scripts/tablegen.ts +++ b/packages/store/ts/scripts/tablegen.ts @@ -3,12 +3,14 @@ import { getRemappings } from "@latticexyz/common/foundry"; import { tablegen } from "../codegen"; import { Store as StoreConfig } from "../config/v2/output"; -const configPath = await resolveConfigPath(undefined); -const config = (await loadConfig(configPath)) as StoreConfig; -const remappings = await getRemappings(); +(async () => { + const configPath = await resolveConfigPath(undefined); + const config = (await loadConfig(configPath)) as StoreConfig; + const remappings = await getRemappings(); -await tablegen({ - configPath, - config, - remappings, -}); + await tablegen({ + configPath, + config, + remappings, + }); +})(); diff --git a/packages/store/tsup.config.ts b/packages/store/tsup.config.ts index 569cd72017..887651005f 100644 --- a/packages/store/tsup.config.ts +++ b/packages/store/tsup.config.ts @@ -11,7 +11,7 @@ export default defineConfig({ register: "ts/register/index.ts", }, target: "esnext", - format: ["esm"], + format: ["esm", "cjs"], dts: !process.env.TSUP_SKIP_DTS, sourcemap: true, clean: true, diff --git a/packages/utils/package.json b/packages/utils/package.json index cdc25f36ad..fa85583237 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -7,7 +7,6 @@ "directory": "packages/utils" }, "license": "MIT", - "type": "module", "exports": { ".": "./dist/index.js" }, diff --git a/packages/utils/tsup.config.ts b/packages/utils/tsup.config.ts index 86fe9a5339..af92ad0d25 100644 --- a/packages/utils/tsup.config.ts +++ b/packages/utils/tsup.config.ts @@ -3,7 +3,7 @@ import { defineConfig } from "tsup"; export default defineConfig({ entry: ["src/index.ts"], target: "esnext", - format: ["esm"], + format: ["esm", "cjs"], dts: !process.env.TSUP_SKIP_DTS, sourcemap: true, clean: true, diff --git a/packages/world-modules/package.json b/packages/world-modules/package.json index d33320e1ab..70433ab0f2 100644 --- a/packages/world-modules/package.json +++ b/packages/world-modules/package.json @@ -8,7 +8,6 @@ "directory": "packages/world-modules" }, "license": "MIT", - "type": "module", "exports": { "./internal/mud.config": "./dist/mud.config.js", "./out/*": "./out/*" diff --git a/packages/world-modules/ts/scripts/tablegen.ts b/packages/world-modules/ts/scripts/tablegen.ts index bac85728d6..1bed6dcd27 100644 --- a/packages/world-modules/ts/scripts/tablegen.ts +++ b/packages/world-modules/ts/scripts/tablegen.ts @@ -3,12 +3,14 @@ import { getRemappings } from "@latticexyz/common/foundry"; import { Store as StoreConfig } from "@latticexyz/store"; import { tablegen } from "@latticexyz/store/codegen"; -const configPath = await resolveConfigPath(undefined); -const config = (await loadConfig(configPath)) as StoreConfig; -const remappings = await getRemappings(); +(async () => { + const configPath = await resolveConfigPath(undefined); + const config = (await loadConfig(configPath)) as StoreConfig; + const remappings = await getRemappings(); -await tablegen({ - configPath, - config, - remappings, -}); + await tablegen({ + configPath, + config, + remappings, + }); +})(); diff --git a/packages/world-modules/ts/scripts/worldgen.ts b/packages/world-modules/ts/scripts/worldgen.ts index 16852d9730..2ce0218996 100644 --- a/packages/world-modules/ts/scripts/worldgen.ts +++ b/packages/world-modules/ts/scripts/worldgen.ts @@ -6,28 +6,30 @@ import { getSrcDirectory } from "@latticexyz/common/foundry"; import { World as WorldConfig } from "@latticexyz/world"; import { worldgen } from "@latticexyz/world/node"; -// TODO dedupe this and cli's worldgen command -const configPath = undefined; -const clean = false; -const srcDir = await getSrcDirectory(); +(async () => { + // TODO dedupe this and cli's worldgen command + const configPath = undefined; + const clean = false; + const srcDir = await getSrcDirectory(); -// Get a list of all contract names -const existingContracts = globSync(`${srcDir}/**/*.sol`) - .sort() - .map((path) => ({ - path, - basename: basename(path, ".sol"), - })); + // Get a list of all contract names + const existingContracts = globSync(`${srcDir}/**/*.sol`) + .sort() + .map((path) => ({ + path, + basename: basename(path, ".sol"), + })); -// Load and resolve the config -const mudConfig = (await loadConfig(configPath)) as WorldConfig; + // Load and resolve the config + const mudConfig = (await loadConfig(configPath)) as WorldConfig; -const outputBaseDirectory = path.join(srcDir, mudConfig.codegen.outputDirectory); + const outputBaseDirectory = path.join(srcDir, mudConfig.codegen.outputDirectory); -// clear the worldgen directory -if (clean) { - rmSync(path.join(outputBaseDirectory, mudConfig.codegen.worldgenDirectory), { recursive: true, force: true }); -} + // clear the worldgen directory + if (clean) { + rmSync(path.join(outputBaseDirectory, mudConfig.codegen.worldgenDirectory), { recursive: true, force: true }); + } -// generate new interfaces -await worldgen(mudConfig, existingContracts, outputBaseDirectory); + // generate new interfaces + await worldgen(mudConfig, existingContracts, outputBaseDirectory); +})(); diff --git a/packages/world-modules/tsup.config.ts b/packages/world-modules/tsup.config.ts index a89281f3d6..5296d1cc4e 100644 --- a/packages/world-modules/tsup.config.ts +++ b/packages/world-modules/tsup.config.ts @@ -5,7 +5,7 @@ export default defineConfig({ "mud.config": "mud.config.ts", }, target: "esnext", - format: ["esm"], + format: ["esm", "cjs"], dts: !process.env.TSUP_SKIP_DTS, sourcemap: true, clean: true, diff --git a/packages/world/package.json b/packages/world/package.json index dc8a4885a5..e3f34ea5b6 100644 --- a/packages/world/package.json +++ b/packages/world/package.json @@ -8,7 +8,6 @@ "directory": "packages/world" }, "license": "MIT", - "type": "module", "exports": { ".": "./dist/index.js", "./internal": "./dist/internal.js", @@ -63,7 +62,7 @@ "test:ci": "pnpm run test" }, "dependencies": { - "@arktype/util": "0.0.29", + "@arktype/util": "0.0.41-cjs", "@latticexyz/common": "workspace:*", "@latticexyz/config": "workspace:*", "@latticexyz/protocol-parser": "workspace:*", diff --git a/packages/world/ts/config/v2/input.ts b/packages/world/ts/config/v2/input.ts index f8e8927047..ef0d848695 100644 --- a/packages/world/ts/config/v2/input.ts +++ b/packages/world/ts/config/v2/input.ts @@ -1,4 +1,3 @@ -import { evaluate } from "@arktype/util"; import { StoreInput, StoreWithShorthandsInput } from "@latticexyz/store/config/v2"; import { DynamicResolution, ValueWithType } from "./dynamicResolution"; @@ -79,27 +78,25 @@ export type CodegenInput = { worldImportPath?: string; }; -export type WorldInput = evaluate< - StoreInput & { - namespaces?: NamespacesInput; - /** - * Contracts named *System will be deployed by default - * as public systems at `namespace/ContractName`, unless overridden - * - * The key is the system name (capitalized). - * The value is a SystemConfig object. - */ - systems?: SystemsInput; - /** System names to exclude from automatic deployment */ - excludeSystems?: readonly string[]; - /** Modules to in the World */ - modules?: readonly ModuleInput[]; - /** Deploy config */ - deploy?: DeployInput; - /** Codegen config */ - codegen?: CodegenInput; - } ->; +export type WorldInput = StoreInput & { + namespaces?: NamespacesInput; + /** + * Contracts named *System will be deployed by default + * as public systems at `namespace/ContractName`, unless overridden + * + * The key is the system name (capitalized). + * The value is a SystemConfig object. + */ + systems?: SystemsInput; + /** System names to exclude from automatic deployment */ + excludeSystems?: readonly string[]; + /** Modules to in the World */ + modules?: readonly ModuleInput[]; + /** Deploy config */ + deploy?: DeployInput; + /** Codegen config */ + codegen?: CodegenInput; +}; export type NamespacesInput = { [key: string]: NamespaceInput }; diff --git a/packages/world/ts/config/v2/world.ts b/packages/world/ts/config/v2/world.ts index a04d90ed42..ca3e81a1a6 100644 --- a/packages/world/ts/config/v2/world.ts +++ b/packages/world/ts/config/v2/world.ts @@ -1,4 +1,4 @@ -import { ErrorMessage, conform, evaluate, narrow } from "@arktype/util"; +import { ErrorMessage, conform, narrow } from "@arktype/util"; import { UserTypes, extendedScope, @@ -44,24 +44,22 @@ export function validateWorld(world: unknown): asserts world is WorldInput { } } -export type resolveWorld = evaluate< - resolveStore & - mergeIfUndefined< - { tables: resolveNamespacedTables } & Omit< - { - [key in keyof world]: key extends "systems" - ? resolveSystems - : key extends "deploy" - ? resolveDeploy - : key extends "codegen" - ? resolveCodegen - : world[key]; - }, - "namespaces" | keyof Store - >, - CONFIG_DEFAULTS - > ->; +export type resolveWorld = resolveStore & + mergeIfUndefined< + { tables: resolveNamespacedTables } & Omit< + { + [key in keyof world]: key extends "systems" + ? resolveSystems + : key extends "deploy" + ? resolveDeploy + : key extends "codegen" + ? resolveCodegen + : world[key]; + }, + "namespaces" | keyof Store + >, + CONFIG_DEFAULTS + >; export function resolveWorld(world: world): resolveWorld { const scope = extendedScope(world); diff --git a/packages/world/ts/scripts/generate-test-tables.ts b/packages/world/ts/scripts/generate-test-tables.ts index 4f310140a6..12c6f71a20 100644 --- a/packages/world/ts/scripts/generate-test-tables.ts +++ b/packages/world/ts/scripts/generate-test-tables.ts @@ -40,6 +40,8 @@ const config = defineWorld({ }, }); -const remappings = await getRemappings(); +(async () => { + const remappings = await getRemappings(); -await tablegen({ configPath, config, remappings }); + await tablegen({ configPath, config, remappings }); +})(); diff --git a/packages/world/ts/scripts/tablegen.ts b/packages/world/ts/scripts/tablegen.ts index bac85728d6..1bed6dcd27 100644 --- a/packages/world/ts/scripts/tablegen.ts +++ b/packages/world/ts/scripts/tablegen.ts @@ -3,12 +3,14 @@ import { getRemappings } from "@latticexyz/common/foundry"; import { Store as StoreConfig } from "@latticexyz/store"; import { tablegen } from "@latticexyz/store/codegen"; -const configPath = await resolveConfigPath(undefined); -const config = (await loadConfig(configPath)) as StoreConfig; -const remappings = await getRemappings(); +(async () => { + const configPath = await resolveConfigPath(undefined); + const config = (await loadConfig(configPath)) as StoreConfig; + const remappings = await getRemappings(); -await tablegen({ - configPath, - config, - remappings, -}); + await tablegen({ + configPath, + config, + remappings, + }); +})(); diff --git a/packages/world/ts/scripts/worldgen.ts b/packages/world/ts/scripts/worldgen.ts index 366db3fae8..00225f8566 100644 --- a/packages/world/ts/scripts/worldgen.ts +++ b/packages/world/ts/scripts/worldgen.ts @@ -6,30 +6,32 @@ import { getSrcDirectory } from "@latticexyz/common/foundry"; import { worldgen } from "../node"; import { World as WorldConfig } from "../config/v2/output"; -// TODO dedupe this and cli's worldgen command -const configPath = undefined; -const clean = false; -const srcDir = await getSrcDirectory(); +(async () => { + // TODO dedupe this and cli's worldgen command + const configPath = undefined; + const clean = false; + const srcDir = await getSrcDirectory(); -// Get a list of all contract names -const existingContracts = globSync(`${srcDir}/**/*.sol`) - .sort() - .map((path) => ({ - path, - basename: basename(path, ".sol"), - })); + // Get a list of all contract names + const existingContracts = globSync(`${srcDir}/**/*.sol`) + .sort() + .map((path) => ({ + path, + basename: basename(path, ".sol"), + })); -// Load and resolve the config -const mudConfig = (await loadConfig(configPath)) as WorldConfig; + // Load and resolve the config + const mudConfig = (await loadConfig(configPath)) as WorldConfig; -const outputBaseDirectory = path.join(srcDir, mudConfig.codegen.outputDirectory); + const outputBaseDirectory = path.join(srcDir, mudConfig.codegen.outputDirectory); -// clear the worldgen directory -if (clean) { - rmSync(path.join(outputBaseDirectory, mudConfig.codegen.worldgenDirectory), { recursive: true, force: true }); -} + // clear the worldgen directory + if (clean) { + rmSync(path.join(outputBaseDirectory, mudConfig.codegen.worldgenDirectory), { recursive: true, force: true }); + } -// generate new interfaces -// override the namespace to be the root namespace for generating the core system interface -const rootMudConfig = { ...mudConfig, namespace: "" }; -await worldgen(rootMudConfig, existingContracts, outputBaseDirectory); + // generate new interfaces + // override the namespace to be the root namespace for generating the core system interface + const rootMudConfig = { ...mudConfig, namespace: "" }; + await worldgen(rootMudConfig, existingContracts, outputBaseDirectory); +})(); diff --git a/packages/world/tsup.config.ts b/packages/world/tsup.config.ts index 8ea323c621..b7b4bb3910 100644 --- a/packages/world/tsup.config.ts +++ b/packages/world/tsup.config.ts @@ -10,7 +10,7 @@ export default defineConfig({ node: "ts/node/index.ts", }, target: "esnext", - format: ["esm"], + format: ["esm", "cjs"], dts: !process.env.TSUP_SKIP_DTS, sourcemap: true, clean: true, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5144109957..6c4f81ed23 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,8 +12,8 @@ importers: specifier: 0.7.5 version: 0.7.5(typescript@5.4.2) '@arktype/util': - specifier: 0.0.43 - version: 0.0.43 + specifier: 0.0.41-cjs + version: 0.0.41-cjs '@changesets/cli': specifier: ^2.26.1 version: 2.26.1 @@ -30,14 +30,14 @@ importers: specifier: ^1.0.11 version: 1.0.11 chalk: - specifier: ^5.2.0 - version: 5.2.0 + specifier: 4.1.2 + version: 4.1.2 eslint: specifier: 8.57.0 version: 8.57.0 execa: - specifier: ^7.0.0 - version: 7.0.0 + specifier: ^5.1.1 + version: 5.1.1 glob: specifier: ^10.4.2 version: 10.4.2 @@ -66,14 +66,14 @@ importers: packages/abi-ts: dependencies: chalk: - specifier: ^5.3.0 - version: 5.3.0 + specifier: 4.1.2 + version: 4.1.2 debug: specifier: ^4.3.4 version: 4.3.4 execa: - specifier: ^7.0.0 - version: 7.0.0 + specifier: ^5.1.1 + version: 5.1.1 glob: specifier: ^10.4.2 version: 10.4.2 @@ -145,11 +145,11 @@ importers: specifier: ^4.3.4 version: 4.3.4 execa: - specifier: ^7.0.0 - version: 7.2.0 + specifier: ^5.1.1 + version: 5.1.1 p-retry: - specifier: ^5.1.2 - version: 5.1.2 + specifier: 4.6.2 + version: 4.6.2 permissionless: specifier: ^0.1.17 version: 0.1.29(viem@2.9.20(bufferutil@4.0.8)(typescript@5.4.2)(zod@3.23.7)) @@ -303,8 +303,8 @@ importers: specifier: ^5.4.1 version: 5.4.1 chalk: - specifier: ^5.0.1 - version: 5.2.0 + specifier: 4.1.2 + version: 4.1.2 chokidar: specifier: ^3.5.3 version: 3.5.3 @@ -318,11 +318,11 @@ importers: specifier: ^5.7.2 version: 5.7.2(bufferutil@4.0.8) execa: - specifier: ^7.0.0 - version: 7.0.0 + specifier: ^5.1.1 + version: 5.1.1 find-up: - specifier: ^6.3.0 - version: 6.3.0 + specifier: 5.0.0 + version: 5.0.0 glob: specifier: ^10.4.2 version: 10.4.2 @@ -330,11 +330,11 @@ importers: specifier: ^1.1.1 version: 1.1.1 p-queue: - specifier: ^7.4.1 - version: 7.4.1 + specifier: 6.6.2 + version: 6.6.2 p-retry: - specifier: ^5.1.2 - version: 5.1.2 + specifier: 4.6.2 + version: 4.6.2 path: specifier: ^0.12.7 version: 0.12.7 @@ -418,14 +418,14 @@ importers: specifier: ^4.3.4 version: 4.3.4 execa: - specifier: ^7.0.0 - version: 7.0.0 + specifier: ^5.1.1 + version: 5.1.1 p-queue: - specifier: ^7.4.1 - version: 7.4.1 + specifier: 6.6.2 + version: 6.6.2 p-retry: - specifier: ^5.1.2 - version: 5.1.2 + specifier: 4.6.2 + version: 4.6.2 prettier: specifier: 3.2.5 version: 3.2.5 @@ -464,8 +464,8 @@ importers: specifier: ^0.17.15 version: 0.17.15 find-up: - specifier: ^6.3.0 - version: 6.3.0 + specifier: 5.0.0 + version: 5.0.0 viem: specifier: 2.9.20 version: 2.9.20(bufferutil@4.0.8)(typescript@5.4.2)(utf-8-validate@6.0.4)(zod@3.23.7) @@ -618,20 +618,20 @@ importers: packages/gas-report: dependencies: chalk: - specifier: ^5.3.0 - version: 5.3.0 + specifier: 4.1.2 + version: 4.1.2 dotenv: specifier: ^16.0.3 version: 16.0.3 execa: - specifier: ^7.0.0 - version: 7.0.0 + specifier: ^5.1.1 + version: 5.1.1 stream-to-array: specifier: ^2.3.0 version: 2.3.0 strip-ansi: - specifier: ^7.1.0 - version: 7.1.0 + specifier: 6.0.1 + version: 6.0.1 table: specifier: ^6.8.1 version: 6.8.1 @@ -844,8 +844,8 @@ importers: packages/store: dependencies: '@arktype/util': - specifier: 0.0.29 - version: 0.0.29 + specifier: 0.0.41-cjs + version: 0.0.41-cjs '@latticexyz/common': specifier: workspace:* version: link:../common @@ -1142,8 +1142,8 @@ importers: packages/world: dependencies: '@arktype/util': - specifier: 0.0.29 - version: 0.0.29 + specifier: 0.0.41-cjs + version: 0.0.41-cjs '@latticexyz/common': specifier: workspace:* version: link:../common @@ -1335,17 +1335,14 @@ packages: '@arktype/schema@0.1.2': resolution: {integrity: sha512-ggvxs5P0toqd/4/XK76URQrtyOYpbYcLhirEZeTso6FxkloPa0lT+whPg7DNQj5qi2OQXLUHBYKMx9DOb13ViQ==} - '@arktype/util@0.0.29': - resolution: {integrity: sha512-fDTBSVzxLj9k1ZjinkawmaQdcXFKMBVK8c+vqMPxwoa94mPMZxBo84yQcqyFVcIcWIkg6qQQmH1ozyT4nqFT/g==} - '@arktype/util@0.0.38': resolution: {integrity: sha512-IvYMGnkUASJllRk3mdBVgckomKx2LNsDTrWCxz04EBK1OuU+4fJ/smSjxgZVWfopNXZds9sHNxZgTJOIw7GvJw==} '@arktype/util@0.0.41': resolution: {integrity: sha512-0YURzJ42v+lhlP1t5Dj90YezETRTCdFU0oM4xMVpYsmPx/DHJzr9n7AX1QPAlYWH4wY7hYY3gwai3O+8VntPgw==} - '@arktype/util@0.0.43': - resolution: {integrity: sha512-tVrvIoghAI/OY6Oc8wo8dpgiZc/a+C8M7NnpSDvfkagAasW9CzLtEbybP1xAALf3A6y5cuJOV6cCcIi9NKQRzA==} + '@arktype/util@0.0.41-cjs': + resolution: {integrity: sha512-JuijNPUx7CSGhw36+z12mrzsR2bRmqeL5W/h0kWlR+hUCgqNc4GLY8JRL5ERg2GwidDQCVe4lvJOpiPffuOCrQ==} '@aws-crypto/ie11-detection@3.0.0': resolution: {integrity: sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==} @@ -4191,8 +4188,8 @@ packages: '@types/react@18.2.22': resolution: {integrity: sha512-60fLTOLqzarLED2O3UQImc/lsNRgG0jE/a1mPW9KjMemY0LMITWEsbS4VvZ4p6rorEHd5YKxxmMKSDK505GHpA==} - '@types/retry@0.12.1': - resolution: {integrity: sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==} + '@types/retry@0.12.0': + resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==} '@types/scheduler@0.16.3': resolution: {integrity: sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==} @@ -5010,14 +5007,6 @@ packages: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} - chalk@5.2.0: - resolution: {integrity: sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==} - engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - - chalk@5.3.0: - resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} - engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - change-case@5.2.0: resolution: {integrity: sha512-L6VzznESnMIKKdKhVzCG+KPz4+x1FWbjOs1AdhoHStV3qo8aySMRGPUoqC0aL1ThKaQNGhAu6ZfHL/QAyQRuiw==} @@ -5871,10 +5860,6 @@ packages: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} - execa@7.0.0: - resolution: {integrity: sha512-tQbH0pH/8LHTnwTrsKWideqi6rFB/QNUawEwrn+WHyz7PX1Tuz2u7wfTvbaNBdP5JD5LVWxNo8/A8CHNZ3bV6g==} - engines: {node: ^14.18.0 || ^16.14.0 || >=18.0.0} - execa@7.2.0: resolution: {integrity: sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==} engines: {node: ^14.18.0 || ^16.14.0 || >=18.0.0} @@ -6035,10 +6020,6 @@ packages: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} - find-up@6.3.0: - resolution: {integrity: sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - find-yarn-workspace-root2@1.2.16: resolution: {integrity: sha512-hr6hb1w8ePMpPVUK39S4RlwJzi+xPLuVuG8XlwXU3KD5Yn3qgBWVfy3AzNlDhWvE1EORCE65/Qm26rFQt3VLVA==} @@ -7204,10 +7185,6 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} - locate-path@7.2.0: - resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - lodash.debounce@4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} @@ -7875,6 +7852,10 @@ packages: resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} engines: {node: '>=8'} + p-finally@1.0.0: + resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} + engines: {node: '>=4'} + p-finally@2.0.1: resolution: {integrity: sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw==} engines: {node: '>=8'} @@ -7907,10 +7888,6 @@ packages: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} - p-locate@6.0.0: - resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - p-map@2.1.0: resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} engines: {node: '>=6'} @@ -7919,17 +7896,17 @@ packages: resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} engines: {node: '>=10'} - p-queue@7.4.1: - resolution: {integrity: sha512-vRpMXmIkYF2/1hLBKisKeVYJZ8S2tZ0zEAmIJgdVKP2nq0nh4qCdf8bgw+ZgKrkh71AOCaqzwbJJk1WtdcF3VA==} - engines: {node: '>=12'} + p-queue@6.6.2: + resolution: {integrity: sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==} + engines: {node: '>=8'} - p-retry@5.1.2: - resolution: {integrity: sha512-couX95waDu98NfNZV+i/iLt+fdVxmI7CbrrdC2uDWfPdUAApyxT4wmDlyOtR5KtTDmkDO0zDScDjDou9YHhd9g==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + p-retry@4.6.2: + resolution: {integrity: sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==} + engines: {node: '>=8'} - p-timeout@5.1.0: - resolution: {integrity: sha512-auFDyzzzGZZZdHz3BtET9VEz0SE/uMEAx7uWfGPucfzEwwe/xH0iVeZibQmANYE/hp9T2+UUZT5m+BKyrDp3Ew==} - engines: {node: '>=12'} + p-timeout@3.2.0: + resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==} + engines: {node: '>=8'} p-try@2.2.0: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} @@ -7968,10 +7945,6 @@ packages: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} - path-exists@5.0.0: - resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - path-is-absolute@1.0.1: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} @@ -10141,13 +10114,11 @@ snapshots: dependencies: '@arktype/util': 0.0.38 - '@arktype/util@0.0.29': {} - '@arktype/util@0.0.38': {} '@arktype/util@0.0.41': {} - '@arktype/util@0.0.43': {} + '@arktype/util@0.0.41-cjs': {} '@aws-crypto/ie11-detection@3.0.0': dependencies: @@ -14198,7 +14169,7 @@ snapshots: '@types/scheduler': 0.16.3 csstype: 3.1.2 - '@types/retry@0.12.1': {} + '@types/retry@0.12.0': {} '@types/scheduler@0.16.3': {} @@ -15428,10 +15399,6 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 - chalk@5.2.0: {} - - chalk@5.3.0: {} - change-case@5.2.0: {} char-regex@1.0.2: {} @@ -16424,18 +16391,6 @@ snapshots: signal-exit: 3.0.7 strip-final-newline: 2.0.0 - execa@7.0.0: - dependencies: - cross-spawn: 7.0.3 - get-stream: 6.0.1 - human-signals: 4.3.1 - is-stream: 3.0.0 - merge-stream: 2.0.0 - npm-run-path: 5.1.0 - onetime: 6.0.0 - signal-exit: 3.0.7 - strip-final-newline: 3.0.0 - execa@7.2.0: dependencies: cross-spawn: 7.0.3 @@ -16654,11 +16609,6 @@ snapshots: locate-path: 6.0.0 path-exists: 4.0.0 - find-up@6.3.0: - dependencies: - locate-path: 7.2.0 - path-exists: 5.0.0 - find-yarn-workspace-root2@1.2.16: dependencies: micromatch: 4.0.5 @@ -18127,10 +18077,6 @@ snapshots: dependencies: p-locate: 5.0.0 - locate-path@7.2.0: - dependencies: - p-locate: 6.0.0 - lodash.debounce@4.0.8: {} lodash.get@4.4.2: {} @@ -18903,6 +18849,8 @@ snapshots: dependencies: p-map: 2.1.0 + p-finally@1.0.0: {} + p-finally@2.0.1: {} p-is-promise@3.0.0: {} @@ -18931,27 +18879,25 @@ snapshots: dependencies: p-limit: 3.1.0 - p-locate@6.0.0: - dependencies: - p-limit: 4.0.0 - p-map@2.1.0: {} p-map@4.0.0: dependencies: aggregate-error: 3.1.0 - p-queue@7.4.1: + p-queue@6.6.2: dependencies: - eventemitter3: 5.0.1 - p-timeout: 5.1.0 + eventemitter3: 4.0.7 + p-timeout: 3.2.0 - p-retry@5.1.2: + p-retry@4.6.2: dependencies: - '@types/retry': 0.12.1 + '@types/retry': 0.12.0 retry: 0.13.1 - p-timeout@5.1.0: {} + p-timeout@3.2.0: + dependencies: + p-finally: 1.0.0 p-try@2.2.0: {} @@ -18985,8 +18931,6 @@ snapshots: path-exists@4.0.0: {} - path-exists@5.0.0: {} - path-is-absolute@1.0.1: {} path-is-inside@1.0.2: {} diff --git a/scripts/changelog.ts b/scripts/changelog.ts index 3dbc23f14f..d566367123 100644 --- a/scripts/changelog.ts +++ b/scripts/changelog.ts @@ -3,7 +3,7 @@ * central changelog (https://github.com/changesets/changesets/issues/1059). */ -import { execa } from "execa"; +import execa from "execa"; import { readFileSync, writeFileSync } from "node:fs"; import path from "path"; import { globSync } from "glob"; diff --git a/scripts/render-api-docs.ts b/scripts/render-api-docs.ts index 8ae48ff7f3..769b454777 100644 --- a/scripts/render-api-docs.ts +++ b/scripts/render-api-docs.ts @@ -2,7 +2,7 @@ * Parse raw `forge doc` output from contract packages, clean it up, and render as markdown in docs. */ -import { execa } from "execa"; +import execa from "execa"; import prettier from "prettier"; import { readFileSync, readdirSync, writeFileSync } from "fs"; import path from "path"; diff --git a/templates/phaser/packages/client/package.json b/templates/phaser/packages/client/package.json index 3b34b585d2..48d13fa12e 100644 --- a/templates/phaser/packages/client/package.json +++ b/templates/phaser/packages/client/package.json @@ -3,7 +3,6 @@ "version": "0.0.0", "private": true, "license": "MIT", - "type": "module", "scripts": { "build": "vite build", "dev": "wait-port localhost:8545 && vite", diff --git a/templates/react-ecs/packages/client/package.json b/templates/react-ecs/packages/client/package.json index 04710c072d..b99625e104 100644 --- a/templates/react-ecs/packages/client/package.json +++ b/templates/react-ecs/packages/client/package.json @@ -3,7 +3,6 @@ "version": "0.0.0", "private": true, "license": "MIT", - "type": "module", "scripts": { "build": "vite build", "dev": "wait-port localhost:8545 && vite", diff --git a/templates/react/packages/client/package.json b/templates/react/packages/client/package.json index a2df9843f2..6e59da4072 100644 --- a/templates/react/packages/client/package.json +++ b/templates/react/packages/client/package.json @@ -3,7 +3,6 @@ "version": "0.0.0", "private": true, "license": "MIT", - "type": "module", "scripts": { "build": "vite build", "dev": "wait-port localhost:8545 && vite", diff --git a/templates/threejs/packages/client/package.json b/templates/threejs/packages/client/package.json index 8bf95ab68d..326e6b5bd0 100644 --- a/templates/threejs/packages/client/package.json +++ b/templates/threejs/packages/client/package.json @@ -3,7 +3,6 @@ "version": "0.0.0", "private": true, "license": "MIT", - "type": "module", "scripts": { "build": "vite build", "dev": "wait-port localhost:8545 && vite", diff --git a/templates/vanilla/packages/client/package.json b/templates/vanilla/packages/client/package.json index 58f660b0f5..a53f41cb13 100644 --- a/templates/vanilla/packages/client/package.json +++ b/templates/vanilla/packages/client/package.json @@ -3,7 +3,6 @@ "version": "0.0.0", "private": true, "license": "MIT", - "type": "module", "scripts": { "build": "vite build", "dev": "wait-port localhost:8545 && vite", From 444cba86dc923691dc7789cebd601df388817940 Mon Sep 17 00:00:00 2001 From: Dhvani Patel Date: Tue, 13 Aug 2024 15:14:19 +0100 Subject: [PATCH 5/8] fix: remove console.trace() --- packages/account-kit/src/transportObserver.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/account-kit/src/transportObserver.ts b/packages/account-kit/src/transportObserver.ts index 4e0e701e06..3c8d7715ef 100644 --- a/packages/account-kit/src/transportObserver.ts +++ b/packages/account-kit/src/transportObserver.ts @@ -6,7 +6,6 @@ export function transportObserver(name: string, tr const result = transport(opts); const request: typeof result.request = async (req) => { debug("request", name, req); - console.trace("request", name, req.method); const response = await result.request(req); debug("response", name, response); return response as never; From d3ec645f11573e6f3be987ec96e770dfbf04a8e1 Mon Sep 17 00:00:00 2001 From: Dhvani Patel Date: Sun, 20 Oct 2024 18:41:38 +0700 Subject: [PATCH 6/8] fix: move up fee ref --- packages/common/src/writeContract.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/common/src/writeContract.ts b/packages/common/src/writeContract.ts index 0da2f24662..5c80dce906 100644 --- a/packages/common/src/writeContract.ts +++ b/packages/common/src/writeContract.ts @@ -139,7 +139,7 @@ export async function writeContract< const nonce = nonceManager.nextNonce(); - const fullRequest = { ...preparedRequest, nonce, ...feeRef.fees }; + const fullRequest = { ...feeRef.fees, ...preparedRequest, nonce }; debug("calling", fullRequest.functionName, "with nonce", nonce, "at", fullRequest.address); return await getAction(client, viem_writeContract, "writeContract")(fullRequest as never); }, From 7b4fcd074a83797487a1081bffdcaa599e400330 Mon Sep 17 00:00:00 2001 From: Dhvani Patel Date: Sun, 20 Oct 2024 20:57:48 +0700 Subject: [PATCH 7/8] fix: request params --- packages/common/src/writeContract.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/common/src/writeContract.ts b/packages/common/src/writeContract.ts index 5c80dce906..9f6a405ed5 100644 --- a/packages/common/src/writeContract.ts +++ b/packages/common/src/writeContract.ts @@ -140,6 +140,12 @@ export async function writeContract< const nonce = nonceManager.nextNonce(); const fullRequest = { ...feeRef.fees, ...preparedRequest, nonce }; + if (request.maxFeePerGas !== undefined && request.maxFeePerGas !== null) { + fullRequest.maxFeePerGas = request.maxFeePerGas; + } + if (request.maxPriorityFeePerGas !== undefined && request.maxPriorityFeePerGas !== null) { + fullRequest.maxPriorityFeePerGas = request.maxPriorityFeePerGas; + } debug("calling", fullRequest.functionName, "with nonce", nonce, "at", fullRequest.address); return await getAction(client, viem_writeContract, "writeContract")(fullRequest as never); }, From bba5e26055474d1342408b17949ec4e412566005 Mon Sep 17 00:00:00 2001 From: Dhvani Patel Date: Wed, 18 Dec 2024 18:56:12 -0700 Subject: [PATCH 8/8] feat: add register erc20 and erc721 method without installing module --- .../modules/erc20-puppet/registerERC20.sol | 20 +++++++++++++++++++ .../modules/erc721-puppet/registerERC721.sol | 20 +++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/packages/world-modules/src/modules/erc20-puppet/registerERC20.sol b/packages/world-modules/src/modules/erc20-puppet/registerERC20.sol index 1fb446ce7c..7471170e9a 100644 --- a/packages/world-modules/src/modules/erc20-puppet/registerERC20.sol +++ b/packages/world-modules/src/modules/erc20-puppet/registerERC20.sol @@ -33,3 +33,23 @@ function registerERC20( // Return the newly created ERC20 token token = IERC20Mintable(ERC20Registry.get(ERC20_REGISTRY_TABLE_ID, WorldResourceIdLib.encodeNamespace(namespace))); } + +/** + * @notice Register a new ERC20 token with the given metadata in a given namespace + * @dev This function must be called within a Store context (i.e. using StoreSwitch.setStoreAddress()) + */ +function registerERC20Strict( + IBaseWorld world, + bytes14 namespace, + ERC20MetadataData memory metadata +) returns (IERC20Mintable token) { + // Get the ERC20 module + ERC20Module erc20Module = ERC20Module(NamespaceOwner.get(MODULE_NAMESPACE_ID)); + require(address(erc20Module) != address(0), "ERC20Module not installed"); + + // Install the ERC20 module with the provided args + world.installModule(erc20Module, abi.encode(namespace, metadata)); + + // Return the newly created ERC20 token + token = IERC20Mintable(ERC20Registry.get(ERC20_REGISTRY_TABLE_ID, WorldResourceIdLib.encodeNamespace(namespace))); +} diff --git a/packages/world-modules/src/modules/erc721-puppet/registerERC721.sol b/packages/world-modules/src/modules/erc721-puppet/registerERC721.sol index 52c43d80eb..6d5f06a98c 100644 --- a/packages/world-modules/src/modules/erc721-puppet/registerERC721.sol +++ b/packages/world-modules/src/modules/erc721-puppet/registerERC721.sol @@ -35,3 +35,23 @@ function registerERC721( // Return the newly created ERC721 token token = IERC721Mintable(ERC721Registry.get(ERC721_REGISTRY_TABLE_ID, WorldResourceIdLib.encodeNamespace(namespace))); } + +/** + * @notice Register a new ERC721 token with the given metadata in a given namespace + * @dev This function must be called within a Store context (i.e. using StoreSwitch.setStoreAddress()) + */ +function registerERC721Strict( + IBaseWorld world, + bytes14 namespace, + ERC721MetadataData memory metadata +) returns (IERC721Mintable token) { + // Get the ERC721 module + ERC721Module erc721Module = ERC721Module(NamespaceOwner.get(MODULE_NAMESPACE_ID)); + require(address(erc721Module) != address(0), "ERC721Module not installed"); + + // Install the ERC721 module with the provided args + world.installModule(erc721Module, abi.encode(namespace, metadata)); + + // Return the newly created ERC721 token + token = IERC721Mintable(ERC721Registry.get(ERC721_REGISTRY_TABLE_ID, WorldResourceIdLib.encodeNamespace(namespace))); +}