From 23079d833b3782759f4e16f5e95d3e2bcba54942 Mon Sep 17 00:00:00 2001 From: dk1a Date: Tue, 28 Mar 2023 17:11:03 +0300 Subject: [PATCH 1/9] feat: add updateInField to Store --- packages/store/src/IStore.sol | 9 ++ packages/store/src/StoreCore.sol | 92 ++++++++++++-- packages/store/src/StoreSwitch.sol | 14 +++ packages/store/src/StoreView.sol | 7 ++ packages/store/test/StoreCore.t.sol | 124 +++++++++++++++++++ packages/world/src/World.sol | 35 ++++++ packages/world/src/interfaces/IWorldCore.sol | 13 ++ packages/world/test/World.t.sol | 45 +++++++ 8 files changed, 332 insertions(+), 7 deletions(-) diff --git a/packages/store/src/IStore.sol b/packages/store/src/IStore.sol index 8112e989e6..ee3b113452 100644 --- a/packages/store/src/IStore.sol +++ b/packages/store/src/IStore.sol @@ -28,6 +28,15 @@ interface IStore { // Push encoded items to the dynamic field at schema index function pushToField(uint256 table, bytes32[] calldata key, uint8 schemaIndex, bytes calldata dataToPush) external; + // Change encoded items within the dynamic field at schema index + function updateInField( + uint256 table, + bytes32[] calldata key, + uint8 schemaIndex, + uint256 startIndex, + bytes calldata dataToSet + ) external; + // Set full record (including full dynamic data) function deleteRecord(uint256 table, bytes32[] memory key) external; diff --git a/packages/store/src/StoreCore.sol b/packages/store/src/StoreCore.sol index e55bf34f97..fc11750d38 100644 --- a/packages/store/src/StoreCore.sol +++ b/packages/store/src/StoreCore.sol @@ -7,7 +7,7 @@ import { Storage } from "./Storage.sol"; import { Memory } from "./Memory.sol"; import { Schema, SchemaLib } from "./Schema.sol"; import { PackedCounter } from "./PackedCounter.sol"; -import { Slice } from "./Slice.sol"; +import { Slice, SliceLib } from "./Slice.sol"; import { Hooks, HooksTableId } from "./tables/Hooks.sol"; import { StoreMetadata } from "./tables/StoreMetadata.sol"; import { IStoreHook } from "./IStore.sol"; @@ -27,9 +27,10 @@ library StoreCore { error StoreCore_TableNotFound(uint256 tableId, string tableIdString); error StoreCore_NotImplemented(); + error StoreCore_NotDynamicField(); error StoreCore_InvalidDataLength(uint256 expected, uint256 received); error StoreCore_InvalidFieldNamesLength(uint256 expected, uint256 received); - error StoreCore_NotDynamicField(); + error StoreCore_DataIndexOverflow(uint256 length, uint256 received); /** * Initialize internal tables. @@ -300,6 +301,54 @@ library StoreCore { } } + function updateInField( + uint256 tableId, + bytes32[] memory key, + uint8 schemaIndex, + uint256 startIndex, + bytes memory dataToSet + ) internal { + Schema schema = getSchema(tableId); + + if (schemaIndex < schema.numStaticFields()) { + revert StoreCore_NotDynamicField(); + } + // index must be checked because it could be arbitrarily large + // (but dataToSet.length can be unchecked - it won't overflow into another slot due to gas costs and hashed slots) + if (startIndex > type(uint16).max) { + revert StoreCore_DataIndexOverflow(type(uint16).max, startIndex); + } + + // TODO add setItem-specific event and hook to avoid the storage read? (https://github.com/latticexyz/mud/issues/444) + bytes memory fullData; + { + bytes memory oldData = StoreCoreInternal._getDynamicField(tableId, key, schemaIndex, schema); + fullData = abi.encodePacked( + SliceLib.getSubslice(oldData, 0, startIndex).toBytes(), + dataToSet, + SliceLib.getSubslice(oldData, startIndex + dataToSet.length, oldData.length).toBytes() + ); + } + + // Emit event to notify indexers + emit StoreSetField(tableId, key, schemaIndex, fullData); + + // Call onBeforeSetField hooks (before modifying the state) + address[] memory hooks = Hooks.get(bytes32(tableId)); + for (uint256 i; i < hooks.length; i++) { + IStoreHook hook = IStoreHook(hooks[i]); + hook.onBeforeSetField(tableId, key, schemaIndex, fullData); + } + + StoreCoreInternal._setDynamicFieldItem(tableId, key, schema, schemaIndex, startIndex, dataToSet); + + // Call onAfterSetField hooks (after modifying the state) + for (uint256 i; i < hooks.length; i++) { + IStoreHook hook = IStoreHook(hooks[i]); + hook.onAfterSetField(tableId, key, schemaIndex, fullData); + } + } + /************************************************************************ * * GET DATA @@ -489,11 +538,22 @@ library StoreCoreInternal { Storage.store({ storagePointer: dynamicSchemaLengthSlot, data: encodedLengths.unwrap() }); // Append `dataToPush` to the end of the data in storage - uint256 dynamicDataLocation = _getDynamicDataLocation(tableId, key, dynamicSchemaIndex); - dynamicDataLocation += oldFieldLength / 32; - // offset for new data (old data never has an offset because each dynamic field starts at a different storage slot) - uint256 offset = oldFieldLength % 32; - Storage.store({ storagePointer: dynamicDataLocation, offset: offset, data: dataToPush }); + _setPartialDynamicData(tableId, key, dynamicSchemaIndex, oldFieldLength, dataToPush); + } + + // startOffset is measured in bytes + function _setDynamicFieldItem( + uint256 tableId, + bytes32[] memory key, + Schema schema, + uint8 schemaIndex, + uint256 startIndex, + bytes memory dataToSet + ) internal { + uint8 dynamicSchemaIndex = schemaIndex - schema.numStaticFields(); + + // Set `dataToSet` at the given index + _setPartialDynamicData(tableId, key, dynamicSchemaIndex, startIndex, dataToSet); } /************************************************************************ @@ -636,6 +696,24 @@ library StoreCoreInternal { // Set the new lengths Storage.store({ storagePointer: dynamicSchemaLengthSlot, data: encodedLengths.unwrap() }); } + + /** + * Modify a part of the dynamic field's data (without changing the field's length) + */ + function _setPartialDynamicData( + uint256 tableId, + bytes32[] memory key, + uint8 dynamicSchemaIndex, + uint256 startIndex, + bytes memory partialData + ) internal { + uint256 dynamicDataLocation = _getDynamicDataLocation(tableId, key, dynamicSchemaIndex); + // start index is in bytes, whereas storage slots are in 32-byte words + dynamicDataLocation += startIndex / 32; + // partial storage slot offset (there is no inherent offset, as each dynamic field starts at its own storage slot) + uint256 offset = startIndex % 32; + Storage.store({ storagePointer: dynamicDataLocation, offset: offset, data: partialData }); + } } // Overloads for single key and some fixed length array keys for better devex diff --git a/packages/store/src/StoreSwitch.sol b/packages/store/src/StoreSwitch.sol index 0a387543b9..de7f626659 100644 --- a/packages/store/src/StoreSwitch.sol +++ b/packages/store/src/StoreSwitch.sol @@ -90,6 +90,20 @@ library StoreSwitch { } } + function updateInField( + uint256 table, + bytes32[] memory key, + uint8 fieldIndex, + uint256 startIndex, + bytes memory dataToSet + ) internal { + if (isDelegateCall()) { + StoreCore.updateInField(table, key, fieldIndex, startIndex, dataToSet); + } else { + IStore(msg.sender).updateInField(table, key, fieldIndex, startIndex, dataToSet); + } + } + function deleteRecord(uint256 table, bytes32[] memory key) internal { if (isDelegateCall()) { StoreCore.deleteRecord(table, key); diff --git a/packages/store/src/StoreView.sol b/packages/store/src/StoreView.sol index 6cd019635c..29031713fc 100644 --- a/packages/store/src/StoreView.sol +++ b/packages/store/src/StoreView.sol @@ -45,6 +45,13 @@ contract StoreView is Store { revert StoreView_NotImplemented(); } + /** + * Not implemented in StoreView + */ + function updateInField(uint256, bytes32[] calldata, uint8, uint256, bytes calldata) public virtual { + revert StoreView_NotImplemented(); + } + /** * Not implemented in StoreView */ diff --git a/packages/store/test/StoreCore.t.sol b/packages/store/test/StoreCore.t.sol index 298272cc51..752866b9fa 100644 --- a/packages/store/test/StoreCore.t.sol +++ b/packages/store/test/StoreCore.t.sol @@ -46,6 +46,17 @@ contract StoreCoreTest is Test, StoreView { StoreCore.pushToField(table, key, schemaIndex, dataToPush); } + // Expose an external updateInField function for testing purposes of indexers (see testHooks) + function updateInField( + uint256 table, + bytes32[] calldata key, + uint8 schemaIndex, + uint256 startIndex, + bytes calldata dataToSet + ) public override { + StoreCore.updateInField(table, key, schemaIndex, startIndex, dataToSet); + } + // Expose an external deleteRecord function for testing purposes of indexers (see testHooks) function deleteRecord(uint256 table, bytes32[] calldata key) public override { StoreCore.deleteRecord(table, key); @@ -657,6 +668,119 @@ contract StoreCoreTest is Test, StoreView { assertEq(StoreCore.getField(table, key, 1), newSecondDataBytes); } + function testUpdateInField() public { + uint256 table = uint256(keccak256("some.table")); + + { + // Register table's schema + Schema schema = SchemaLib.encode(SchemaType.UINT256, SchemaType.UINT32_ARRAY, SchemaType.UINT64_ARRAY); + StoreCore.registerSchema(table, schema, defaultKeySchema); + } + + // Create key + bytes32[] memory key = new bytes32[](1); + key[0] = bytes32("some.key"); + + // Create data + bytes32 firstDataBytes = keccak256("some data"); + uint32[] memory secondData = new uint32[](2); + secondData[0] = 0x11121314; + secondData[1] = 0x15161718; + bytes memory secondDataBytes = EncodeArray.encode(secondData); + + uint64[] memory thirdData = new uint64[](6); + thirdData[0] = 0x1111111111111111; + thirdData[1] = 0x2222222222222222; + thirdData[2] = 0x3333333333333333; + thirdData[3] = 0x4444444444444444; + thirdData[4] = 0x5555555555555555; + thirdData[5] = 0x6666666666666666; + bytes memory thirdDataBytes = EncodeArray.encode(thirdData); + + // Set fields + StoreCore.setField(table, key, 0, abi.encodePacked(firstDataBytes)); + StoreCore.setField(table, key, 1, secondDataBytes); + StoreCore.setField(table, key, 2, thirdDataBytes); + + // Create data to use for the update + bytes memory secondDataForUpdate; + bytes memory newSecondDataBytes; + { + uint32[] memory _secondDataForUpdate = new uint32[](1); + _secondDataForUpdate[0] = 0x25262728; + secondDataForUpdate = EncodeArray.encode(_secondDataForUpdate); + + newSecondDataBytes = abi.encodePacked(secondData[0], _secondDataForUpdate[0]); + } + + // Expect a StoreSetField event to be emitted + vm.expectEmit(true, true, true, true); + emit StoreSetField(table, key, 1, newSecondDataBytes); + + // Update index 1 in second field (4 = byte length of uint32) + // !gasreport update in field (1 slot, 1 uint32 item) + StoreCore.updateInField(table, key, 1, 4 * 1, secondDataForUpdate); + + // Get second field + bytes memory loadedData = StoreCore.getField(table, key, 1); + + // Verify loaded data is correct + assertEq(SliceLib.fromBytes(loadedData).decodeArray_uint32().length, secondData.length); + assertEq(loadedData.length, newSecondDataBytes.length); + assertEq(loadedData, newSecondDataBytes); + + // Verify none of the other fields were impacted + assertEq(bytes32(StoreCore.getField(table, key, 0)), firstDataBytes); + assertEq(StoreCore.getField(table, key, 2), thirdDataBytes); + + // Create data for update + bytes memory thirdDataForUpdate; + bytes memory newThirdDataBytes; + { + uint64[] memory _thirdDataForUpdate = new uint64[](4); + _thirdDataForUpdate[0] = 0x7777777777777777; + _thirdDataForUpdate[1] = 0x8888888888888888; + _thirdDataForUpdate[2] = 0x9999999999999999; + _thirdDataForUpdate[3] = 0x0; + thirdDataForUpdate = EncodeArray.encode(_thirdDataForUpdate); + + newThirdDataBytes = abi.encodePacked( + thirdData[0], + _thirdDataForUpdate[0], + _thirdDataForUpdate[1], + _thirdDataForUpdate[2], + _thirdDataForUpdate[3], + thirdData[5] + ); + } + + // Expect a StoreSetField event to be emitted + vm.expectEmit(true, true, true, true); + emit StoreSetField(table, key, 2, newThirdDataBytes); + + // Update indexes 1,2,3,4 in third field (8 = byte length of uint64) + // !gasreport push to field (2 slots, 6 uint64 items) + StoreCore.updateInField(table, key, 2, 8 * 1, thirdDataForUpdate); + + // Get third field + loadedData = StoreCore.getField(table, key, 2); + + // Verify loaded data is correct + assertEq(SliceLib.fromBytes(loadedData).decodeArray_uint64().length, thirdData.length); + assertEq(loadedData.length, newThirdDataBytes.length); + assertEq(loadedData, newThirdDataBytes); + + // Verify none of the other fields were impacted + assertEq(bytes32(StoreCore.getField(table, key, 0)), firstDataBytes); + assertEq(StoreCore.getField(table, key, 1), newSecondDataBytes); + + // startIndex must not overflow + vm.expectRevert( + abi.encodeWithSelector(StoreCore.StoreCore_DataIndexOverflow.selector, type(uint16).max, type(uint32).max) + ); + StoreCore.updateInField(table, key, 2, type(uint32).max, thirdDataForUpdate); + } + function testAccessEmptyData() public { uint256 table = uint256(keccak256("some.table")); Schema schema = SchemaLib.encode(SchemaType.UINT32, SchemaType.UINT32_ARRAY); diff --git a/packages/world/src/World.sol b/packages/world/src/World.sol index 577cad4500..aa9a4153c5 100644 --- a/packages/world/src/World.sol +++ b/packages/world/src/World.sol @@ -163,6 +163,25 @@ contract World is Store, IWorldCore, IErrors { StoreCore.pushToField(resourceSelector.toTableId(), key, schemaIndex, dataToPush); } + /** + * Update data at `startIndex` of a field in the table at the given namespace and file. + * Requires the caller to have access to the namespace or file. + */ + function updateInField( + bytes16 namespace, + bytes16 file, + bytes32[] calldata key, + uint8 schemaIndex, + uint256 startIndex, + bytes calldata dataToSet + ) public virtual { + // Require access to namespace or file + bytes32 resourceSelector = AccessControl.requireAccess(namespace, file, msg.sender); + + // Update data in the field + StoreCore.updateInField(resourceSelector.toTableId(), key, schemaIndex, startIndex, dataToSet); + } + /** * Delete a record in the table at the given namespace and file. * Requires the caller to have access to the namespace or file. @@ -294,6 +313,22 @@ contract World is Store, IWorldCore, IErrors { pushToField(resourceSelector.getNamespace(), resourceSelector.getFile(), key, schemaIndex, dataToPush); } + /** + * Update data at `startIndex` of a field in the table at the given tableId. + * This overload exists to conform with the `IStore` interface. + * The tableId is converted to a resourceSelector, and access is checked based on the namespace or file. + */ + function updateInField( + uint256 tableId, + bytes32[] calldata key, + uint8 schemaIndex, + uint256 startIndex, + bytes calldata dataToSet + ) public virtual { + bytes32 resourceSelector = ResourceSelector.from(tableId); + updateInField(resourceSelector.getNamespace(), resourceSelector.getFile(), key, schemaIndex, startIndex, dataToSet); + } + /** * Delete a record in the table at the given tableId. * This overload exists to conform with the `IStore` interface. diff --git a/packages/world/src/interfaces/IWorldCore.sol b/packages/world/src/interfaces/IWorldCore.sol index ba925d571c..e01a0a12ab 100644 --- a/packages/world/src/interfaces/IWorldCore.sol +++ b/packages/world/src/interfaces/IWorldCore.sol @@ -83,6 +83,19 @@ interface IWorldCore { bytes calldata dataToPush ) external; + /** + * Update data at `startIndex` of a field in the table at the given namespace and file. + * Requires the caller to have access to the namespace or file. + */ + function updateInField( + bytes16 namespace, + bytes16 file, + bytes32[] calldata key, + uint8 schemaIndex, + uint256 startIndex, + bytes calldata dataToSet + ) external; + /** * Delete a record in the table at the given namespace and file. * Requires the caller to have access to the namespace or file. diff --git a/packages/world/test/World.t.sol b/packages/world/test/World.t.sol index e61d740046..6318b0958f 100644 --- a/packages/world/test/World.t.sol +++ b/packages/world/test/World.t.sol @@ -395,6 +395,51 @@ contract WorldTest is Test { world.pushToField(tableId, keyTuple, 0, encodedData); } + function testUpdateInField() public { + bytes16 namespace = "testUpdInField"; + bytes16 file = "testTable"; + + // Register a new table + bytes32 resourceSelector = world.registerTable(namespace, file, AddressArray.getSchema(), defaultKeySchema); + uint256 tableId = uint256(resourceSelector); + + // Create data + address[] memory initData = new address[](3); + initData[0] = address(0x01); + initData[1] = address(bytes20(keccak256("some address"))); + initData[2] = address(bytes20(keccak256("another address"))); + bytes memory encodedData = EncodeArray.encode(initData); + + world.setField(namespace, file, keyTuple, 0, encodedData); + + // Expect the data to be written + assertEq(AddressArray.get(world, tableId, key), initData); + + // Update index 0 + address[] memory dataForUpdate = new address[](1); + dataForUpdate[0] = address(bytes20(keccak256("address for update"))); + world.updateInField(namespace, file, keyTuple, 0, 0, EncodeArray.encode(dataForUpdate)); + + // Expect the data to be updated + initData[0] = dataForUpdate[0]; + assertEq(AddressArray.get(world, tableId, key), initData); + + // Update index 1 via direct access + world.updateInField(tableId, keyTuple, 0, 20 * 1, EncodeArray.encode(dataForUpdate)); + + // Expect the data to be updated + initData[1] = dataForUpdate[0]; + assertEq(AddressArray.get(world, tableId, key), initData); + + // Expect an error when trying to write from an address that doesn't have access (via namespace/file) + _expectAccessDenied(address(0x01), namespace, file); + world.updateInField(namespace, file, keyTuple, 0, 0, EncodeArray.encode(dataForUpdate)); + + // Expect an error when trying to write from an address that doesn't have access (via tableId) + _expectAccessDenied(address(0x01), namespace, file); + world.updateInField(tableId, keyTuple, 0, 0, EncodeArray.encode(dataForUpdate)); + } + function testDeleteRecord() public { bytes16 namespace = "testDeleteRecord"; bytes16 file = "testTable"; From 958153d4d95d8c7402d2bfc138691791208cc12b Mon Sep 17 00:00:00 2001 From: dk1a Date: Tue, 28 Mar 2023 17:13:02 +0300 Subject: [PATCH 2/9] feat(cli): update in tablegen --- packages/cli/src/render-solidity/field.ts | 27 +++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/render-solidity/field.ts b/packages/cli/src/render-solidity/field.ts index c0a2a5fae7..ad676a36e8 100644 --- a/packages/cli/src/render-solidity/field.ts +++ b/packages/cli/src/render-solidity/field.ts @@ -41,8 +41,6 @@ export function renderFieldMethods(options: RenderTableOptions) { ` ); - // TODO: this is super inefficient right now, need to add support for pushing to arrays to the store core library to avoid reading/writing the entire array - // (see https://github.com/latticexyz/mud/issues/438) if (field.isDynamic) { const portionData = fieldPortionData(field); @@ -61,6 +59,29 @@ export function renderFieldMethods(options: RenderTableOptions) { } ` ); + + result += renderWithStore( + storeArgument, + (_typedStore, _store, _commentSuffix) => ` + /** Push ${portionData.title} to ${field.name}${_commentSuffix} */ + function update${field.methodNameSuffix}(${renderArguments([ + _typedStore, + _typedTableId, + _typedKeyArgs, + "uint256 _index", + `${portionData.typeWithLocation} ${portionData.name}`, + ])}) internal { + ${_primaryKeysDefinition} + ${_store}.updateInField( + _tableId, + _primaryKeys, + ${index}, + _index * ${portionData.elementLength}, + ${portionData.encoded} + ); + } + ` + ); } } return result; @@ -109,6 +130,7 @@ function fieldPortionData(field: RenderTableField) { name: "_element", encoded: renderEncodeField({ ...field.arrayElement, arrayElement: undefined, name, methodNameSuffix }), title: "an element", + elementLength: field.arrayElement.staticByteLength, }; } else { const name = "_slice"; @@ -117,6 +139,7 @@ function fieldPortionData(field: RenderTableField) { name, encoded: renderEncodeField({ ...field, name, methodNameSuffix }), title: "a slice", + elementLength: 1, }; } } From 9e4532f5e5580e4ff51828c18e4c952853012b82 Mon Sep 17 00:00:00 2001 From: dk1a Date: Tue, 28 Mar 2023 17:13:12 +0300 Subject: [PATCH 3/9] chore: update tables --- .../cli/contracts/src/tables/Dynamics.sol | 64 +++++++++++++++++++ .../cli/contracts/src/tables/Singleton.sol | 21 ++++++ packages/store/src/tables/Callbacks.sol | 8 +++ packages/store/src/tables/Hooks.sol | 8 +++ packages/store/src/tables/Mixed.sol | 16 +++++ packages/store/src/tables/StoreMetadata.sol | 32 ++++++++++ .../keyswithvalue/tables/KeysWithValue.sol | 16 +++++ packages/world/src/tables/AddressArray.sol | 16 +++++ 8 files changed, 181 insertions(+) diff --git a/packages/cli/contracts/src/tables/Dynamics.sol b/packages/cli/contracts/src/tables/Dynamics.sol index 1241dc712b..48196efce1 100644 --- a/packages/cli/contracts/src/tables/Dynamics.sol +++ b/packages/cli/contracts/src/tables/Dynamics.sol @@ -104,6 +104,14 @@ library Dynamics { StoreSwitch.pushToField(_tableId, _primaryKeys, 0, abi.encodePacked((_element))); } + /** Push an element to staticB32 */ + function updateStaticB32(bytes32 key, uint256 _index, bytes32 _element) internal { + bytes32[] memory _primaryKeys = new bytes32[](1); + _primaryKeys[0] = bytes32((key)); + + StoreSwitch.updateInField(_tableId, _primaryKeys, 0, _index * 32, abi.encodePacked((_element))); + } + /** Get staticI32 */ function getStaticI32(bytes32 key) internal view returns (int32[2] memory staticI32) { bytes32[] memory _primaryKeys = new bytes32[](1); @@ -129,6 +137,14 @@ library Dynamics { StoreSwitch.pushToField(_tableId, _primaryKeys, 1, abi.encodePacked((_element))); } + /** Push an element to staticI32 */ + function updateStaticI32(bytes32 key, uint256 _index, int32 _element) internal { + bytes32[] memory _primaryKeys = new bytes32[](1); + _primaryKeys[0] = bytes32((key)); + + StoreSwitch.updateInField(_tableId, _primaryKeys, 1, _index * 4, abi.encodePacked((_element))); + } + /** Get staticU128 */ function getStaticU128(bytes32 key) internal view returns (uint128[3] memory staticU128) { bytes32[] memory _primaryKeys = new bytes32[](1); @@ -154,6 +170,14 @@ library Dynamics { StoreSwitch.pushToField(_tableId, _primaryKeys, 2, abi.encodePacked((_element))); } + /** Push an element to staticU128 */ + function updateStaticU128(bytes32 key, uint256 _index, uint128 _element) internal { + bytes32[] memory _primaryKeys = new bytes32[](1); + _primaryKeys[0] = bytes32((key)); + + StoreSwitch.updateInField(_tableId, _primaryKeys, 2, _index * 16, abi.encodePacked((_element))); + } + /** Get staticAddrs */ function getStaticAddrs(bytes32 key) internal view returns (address[4] memory staticAddrs) { bytes32[] memory _primaryKeys = new bytes32[](1); @@ -179,6 +203,14 @@ library Dynamics { StoreSwitch.pushToField(_tableId, _primaryKeys, 3, abi.encodePacked((_element))); } + /** Push an element to staticAddrs */ + function updateStaticAddrs(bytes32 key, uint256 _index, address _element) internal { + bytes32[] memory _primaryKeys = new bytes32[](1); + _primaryKeys[0] = bytes32((key)); + + StoreSwitch.updateInField(_tableId, _primaryKeys, 3, _index * 20, abi.encodePacked((_element))); + } + /** Get staticBools */ function getStaticBools(bytes32 key) internal view returns (bool[5] memory staticBools) { bytes32[] memory _primaryKeys = new bytes32[](1); @@ -204,6 +236,14 @@ library Dynamics { StoreSwitch.pushToField(_tableId, _primaryKeys, 4, abi.encodePacked((_element))); } + /** Push an element to staticBools */ + function updateStaticBools(bytes32 key, uint256 _index, bool _element) internal { + bytes32[] memory _primaryKeys = new bytes32[](1); + _primaryKeys[0] = bytes32((key)); + + StoreSwitch.updateInField(_tableId, _primaryKeys, 4, _index * 1, abi.encodePacked((_element))); + } + /** Get u64 */ function getU64(bytes32 key) internal view returns (uint64[] memory u64) { bytes32[] memory _primaryKeys = new bytes32[](1); @@ -229,6 +269,14 @@ library Dynamics { StoreSwitch.pushToField(_tableId, _primaryKeys, 5, abi.encodePacked((_element))); } + /** Push an element to u64 */ + function updateU64(bytes32 key, uint256 _index, uint64 _element) internal { + bytes32[] memory _primaryKeys = new bytes32[](1); + _primaryKeys[0] = bytes32((key)); + + StoreSwitch.updateInField(_tableId, _primaryKeys, 5, _index * 8, abi.encodePacked((_element))); + } + /** Get str */ function getStr(bytes32 key) internal view returns (string memory str) { bytes32[] memory _primaryKeys = new bytes32[](1); @@ -254,6 +302,14 @@ library Dynamics { StoreSwitch.pushToField(_tableId, _primaryKeys, 6, bytes((_slice))); } + /** Push a slice to str */ + function updateStr(bytes32 key, uint256 _index, string memory _slice) internal { + bytes32[] memory _primaryKeys = new bytes32[](1); + _primaryKeys[0] = bytes32((key)); + + StoreSwitch.updateInField(_tableId, _primaryKeys, 6, _index * 1, bytes((_slice))); + } + /** Get b */ function getB(bytes32 key) internal view returns (bytes memory b) { bytes32[] memory _primaryKeys = new bytes32[](1); @@ -279,6 +335,14 @@ library Dynamics { StoreSwitch.pushToField(_tableId, _primaryKeys, 7, bytes((_slice))); } + /** Push a slice to b */ + function updateB(bytes32 key, uint256 _index, bytes memory _slice) internal { + bytes32[] memory _primaryKeys = new bytes32[](1); + _primaryKeys[0] = bytes32((key)); + + StoreSwitch.updateInField(_tableId, _primaryKeys, 7, _index * 1, bytes((_slice))); + } + /** Get the full data */ function get(bytes32 key) internal view returns (DynamicsData memory _table) { bytes32[] memory _primaryKeys = new bytes32[](1); diff --git a/packages/cli/contracts/src/tables/Singleton.sol b/packages/cli/contracts/src/tables/Singleton.sol index 90c29c60aa..231d764ddf 100644 --- a/packages/cli/contracts/src/tables/Singleton.sol +++ b/packages/cli/contracts/src/tables/Singleton.sol @@ -96,6 +96,13 @@ library Singleton { StoreSwitch.pushToField(_tableId, _primaryKeys, 1, abi.encodePacked((_element))); } + /** Push an element to v2 */ + function updateV2(uint256 _index, uint32 _element) internal { + bytes32[] memory _primaryKeys = new bytes32[](0); + + StoreSwitch.updateInField(_tableId, _primaryKeys, 1, _index * 4, abi.encodePacked((_element))); + } + /** Get v3 */ function getV3() internal view returns (uint32[2] memory v3) { bytes32[] memory _primaryKeys = new bytes32[](0); @@ -118,6 +125,13 @@ library Singleton { StoreSwitch.pushToField(_tableId, _primaryKeys, 2, abi.encodePacked((_element))); } + /** Push an element to v3 */ + function updateV3(uint256 _index, uint32 _element) internal { + bytes32[] memory _primaryKeys = new bytes32[](0); + + StoreSwitch.updateInField(_tableId, _primaryKeys, 2, _index * 4, abi.encodePacked((_element))); + } + /** Get v4 */ function getV4() internal view returns (uint32[1] memory v4) { bytes32[] memory _primaryKeys = new bytes32[](0); @@ -140,6 +154,13 @@ library Singleton { StoreSwitch.pushToField(_tableId, _primaryKeys, 3, abi.encodePacked((_element))); } + /** Push an element to v4 */ + function updateV4(uint256 _index, uint32 _element) internal { + bytes32[] memory _primaryKeys = new bytes32[](0); + + StoreSwitch.updateInField(_tableId, _primaryKeys, 3, _index * 4, abi.encodePacked((_element))); + } + /** Get the full data */ function get() internal view returns (int256 v1, uint32[2] memory v2, uint32[2] memory v3, uint32[1] memory v4) { bytes32[] memory _primaryKeys = new bytes32[](0); diff --git a/packages/store/src/tables/Callbacks.sol b/packages/store/src/tables/Callbacks.sol index 085f5961e3..86a9e884e1 100644 --- a/packages/store/src/tables/Callbacks.sol +++ b/packages/store/src/tables/Callbacks.sol @@ -79,6 +79,14 @@ library Callbacks { StoreSwitch.pushToField(_tableId, _primaryKeys, 0, abi.encodePacked((_element))); } + /** Push an element to value */ + function update(bytes32 key, uint256 _index, bytes24 _element) internal { + bytes32[] memory _primaryKeys = new bytes32[](1); + _primaryKeys[0] = bytes32((key)); + + StoreSwitch.updateInField(_tableId, _primaryKeys, 0, _index * 24, abi.encodePacked((_element))); + } + /** Tightly pack full data using this table's schema */ function encode(bytes24[] memory value) internal view returns (bytes memory) { uint16[] memory _counters = new uint16[](1); diff --git a/packages/store/src/tables/Hooks.sol b/packages/store/src/tables/Hooks.sol index 052cfd28b5..7e437de213 100644 --- a/packages/store/src/tables/Hooks.sol +++ b/packages/store/src/tables/Hooks.sol @@ -79,6 +79,14 @@ library Hooks { StoreSwitch.pushToField(_tableId, _primaryKeys, 0, abi.encodePacked((_element))); } + /** Push an element to value */ + function update(bytes32 key, uint256 _index, address _element) internal { + bytes32[] memory _primaryKeys = new bytes32[](1); + _primaryKeys[0] = bytes32((key)); + + StoreSwitch.updateInField(_tableId, _primaryKeys, 0, _index * 20, abi.encodePacked((_element))); + } + /** Tightly pack full data using this table's schema */ function encode(address[] memory value) internal view returns (bytes memory) { uint16[] memory _counters = new uint16[](1); diff --git a/packages/store/src/tables/Mixed.sol b/packages/store/src/tables/Mixed.sol index 6570e65d6b..084de97b95 100644 --- a/packages/store/src/tables/Mixed.sol +++ b/packages/store/src/tables/Mixed.sol @@ -126,6 +126,14 @@ library Mixed { StoreSwitch.pushToField(_tableId, _primaryKeys, 2, abi.encodePacked((_element))); } + /** Push an element to a32 */ + function updateA32(bytes32 key, uint256 _index, uint32 _element) internal { + bytes32[] memory _primaryKeys = new bytes32[](1); + _primaryKeys[0] = bytes32((key)); + + StoreSwitch.updateInField(_tableId, _primaryKeys, 2, _index * 4, abi.encodePacked((_element))); + } + /** Get s */ function getS(bytes32 key) internal view returns (string memory s) { bytes32[] memory _primaryKeys = new bytes32[](1); @@ -151,6 +159,14 @@ library Mixed { StoreSwitch.pushToField(_tableId, _primaryKeys, 3, bytes((_slice))); } + /** Push a slice to s */ + function updateS(bytes32 key, uint256 _index, string memory _slice) internal { + bytes32[] memory _primaryKeys = new bytes32[](1); + _primaryKeys[0] = bytes32((key)); + + StoreSwitch.updateInField(_tableId, _primaryKeys, 3, _index * 1, bytes((_slice))); + } + /** Get the full data */ function get(bytes32 key) internal view returns (MixedData memory _table) { bytes32[] memory _primaryKeys = new bytes32[](1); diff --git a/packages/store/src/tables/StoreMetadata.sol b/packages/store/src/tables/StoreMetadata.sol index 8b141af6dd..65a87e06d5 100644 --- a/packages/store/src/tables/StoreMetadata.sol +++ b/packages/store/src/tables/StoreMetadata.sol @@ -122,6 +122,22 @@ library StoreMetadata { _store.pushToField(_tableId, _primaryKeys, 0, bytes((_slice))); } + /** Push a slice to tableName */ + function updateTableName(uint256 tableId, uint256 _index, string memory _slice) internal { + bytes32[] memory _primaryKeys = new bytes32[](1); + _primaryKeys[0] = bytes32(uint256((tableId))); + + StoreSwitch.updateInField(_tableId, _primaryKeys, 0, _index * 1, bytes((_slice))); + } + + /** Push a slice to tableName (using the specified store) */ + function updateTableName(IStore _store, uint256 tableId, uint256 _index, string memory _slice) internal { + bytes32[] memory _primaryKeys = new bytes32[](1); + _primaryKeys[0] = bytes32(uint256((tableId))); + + _store.updateInField(_tableId, _primaryKeys, 0, _index * 1, bytes((_slice))); + } + /** Get abiEncodedFieldNames */ function getAbiEncodedFieldNames(uint256 tableId) internal view returns (bytes memory abiEncodedFieldNames) { bytes32[] memory _primaryKeys = new bytes32[](1); @@ -175,6 +191,22 @@ library StoreMetadata { _store.pushToField(_tableId, _primaryKeys, 1, bytes((_slice))); } + /** Push a slice to abiEncodedFieldNames */ + function updateAbiEncodedFieldNames(uint256 tableId, uint256 _index, bytes memory _slice) internal { + bytes32[] memory _primaryKeys = new bytes32[](1); + _primaryKeys[0] = bytes32(uint256((tableId))); + + StoreSwitch.updateInField(_tableId, _primaryKeys, 1, _index * 1, bytes((_slice))); + } + + /** Push a slice to abiEncodedFieldNames (using the specified store) */ + function updateAbiEncodedFieldNames(IStore _store, uint256 tableId, uint256 _index, bytes memory _slice) internal { + bytes32[] memory _primaryKeys = new bytes32[](1); + _primaryKeys[0] = bytes32(uint256((tableId))); + + _store.updateInField(_tableId, _primaryKeys, 1, _index * 1, bytes((_slice))); + } + /** Get the full data */ function get(uint256 tableId) internal view returns (StoreMetadataData memory _table) { bytes32[] memory _primaryKeys = new bytes32[](1); diff --git a/packages/world/src/modules/keyswithvalue/tables/KeysWithValue.sol b/packages/world/src/modules/keyswithvalue/tables/KeysWithValue.sol index 5919891179..bdbb3e6881 100644 --- a/packages/world/src/modules/keyswithvalue/tables/KeysWithValue.sol +++ b/packages/world/src/modules/keyswithvalue/tables/KeysWithValue.sol @@ -116,6 +116,22 @@ library KeysWithValue { _store.pushToField(_tableId, _primaryKeys, 0, abi.encodePacked((_element))); } + /** Push an element to keysWithValue */ + function update(uint256 _tableId, bytes32 valueHash, uint256 _index, bytes32 _element) internal { + bytes32[] memory _primaryKeys = new bytes32[](1); + _primaryKeys[0] = bytes32((valueHash)); + + StoreSwitch.updateInField(_tableId, _primaryKeys, 0, _index * 32, abi.encodePacked((_element))); + } + + /** Push an element to keysWithValue (using the specified store) */ + function update(IStore _store, uint256 _tableId, bytes32 valueHash, uint256 _index, bytes32 _element) internal { + bytes32[] memory _primaryKeys = new bytes32[](1); + _primaryKeys[0] = bytes32((valueHash)); + + _store.updateInField(_tableId, _primaryKeys, 0, _index * 32, abi.encodePacked((_element))); + } + /** Tightly pack full data using this table's schema */ function encode(bytes32[] memory keysWithValue) internal view returns (bytes memory) { uint16[] memory _counters = new uint16[](1); diff --git a/packages/world/src/tables/AddressArray.sol b/packages/world/src/tables/AddressArray.sol index 514d299f4f..915400de98 100644 --- a/packages/world/src/tables/AddressArray.sol +++ b/packages/world/src/tables/AddressArray.sol @@ -112,6 +112,22 @@ library AddressArray { _store.pushToField(_tableId, _primaryKeys, 0, abi.encodePacked((_element))); } + /** Push an element to value */ + function update(uint256 _tableId, bytes32 key, uint256 _index, address _element) internal { + bytes32[] memory _primaryKeys = new bytes32[](1); + _primaryKeys[0] = bytes32((key)); + + StoreSwitch.updateInField(_tableId, _primaryKeys, 0, _index * 20, abi.encodePacked((_element))); + } + + /** Push an element to value (using the specified store) */ + function update(IStore _store, uint256 _tableId, bytes32 key, uint256 _index, address _element) internal { + bytes32[] memory _primaryKeys = new bytes32[](1); + _primaryKeys[0] = bytes32((key)); + + _store.updateInField(_tableId, _primaryKeys, 0, _index * 20, abi.encodePacked((_element))); + } + /** Tightly pack full data using this table's schema */ function encode(address[] memory value) internal view returns (bytes memory) { uint16[] memory _counters = new uint16[](1); From 83d3abee9521bc03ac05f6ab7aec3bb2de4b4a19 Mon Sep 17 00:00:00 2001 From: dk1a Date: Tue, 28 Mar 2023 17:15:23 +0300 Subject: [PATCH 4/9] chore(cli): fix comment --- packages/cli/src/render-solidity/field.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/src/render-solidity/field.ts b/packages/cli/src/render-solidity/field.ts index ad676a36e8..6a3ca0b71d 100644 --- a/packages/cli/src/render-solidity/field.ts +++ b/packages/cli/src/render-solidity/field.ts @@ -63,7 +63,7 @@ export function renderFieldMethods(options: RenderTableOptions) { result += renderWithStore( storeArgument, (_typedStore, _store, _commentSuffix) => ` - /** Push ${portionData.title} to ${field.name}${_commentSuffix} */ + /** Update ${portionData.title} of ${field.name}${_commentSuffix} at \`_index\` */ function update${field.methodNameSuffix}(${renderArguments([ _typedStore, _typedTableId, From e0a4d5d9d3141eb2133b4b0efca64085c961295f Mon Sep 17 00:00:00 2001 From: dk1a Date: Tue, 28 Mar 2023 17:18:58 +0300 Subject: [PATCH 5/9] chore: update tables --- packages/cli/contracts/src/tables/Dynamics.sol | 16 ++++++++-------- packages/cli/contracts/src/tables/Singleton.sol | 6 +++--- packages/store/src/tables/Callbacks.sol | 2 +- packages/store/src/tables/Hooks.sol | 2 +- packages/store/src/tables/Mixed.sol | 4 ++-- packages/store/src/tables/StoreMetadata.sol | 8 ++++---- .../keyswithvalue/tables/KeysWithValue.sol | 4 ++-- packages/world/src/tables/AddressArray.sol | 4 ++-- 8 files changed, 23 insertions(+), 23 deletions(-) diff --git a/packages/cli/contracts/src/tables/Dynamics.sol b/packages/cli/contracts/src/tables/Dynamics.sol index 48196efce1..c0fb32d3d6 100644 --- a/packages/cli/contracts/src/tables/Dynamics.sol +++ b/packages/cli/contracts/src/tables/Dynamics.sol @@ -104,7 +104,7 @@ library Dynamics { StoreSwitch.pushToField(_tableId, _primaryKeys, 0, abi.encodePacked((_element))); } - /** Push an element to staticB32 */ + /** Update an element of staticB32 at `_index` */ function updateStaticB32(bytes32 key, uint256 _index, bytes32 _element) internal { bytes32[] memory _primaryKeys = new bytes32[](1); _primaryKeys[0] = bytes32((key)); @@ -137,7 +137,7 @@ library Dynamics { StoreSwitch.pushToField(_tableId, _primaryKeys, 1, abi.encodePacked((_element))); } - /** Push an element to staticI32 */ + /** Update an element of staticI32 at `_index` */ function updateStaticI32(bytes32 key, uint256 _index, int32 _element) internal { bytes32[] memory _primaryKeys = new bytes32[](1); _primaryKeys[0] = bytes32((key)); @@ -170,7 +170,7 @@ library Dynamics { StoreSwitch.pushToField(_tableId, _primaryKeys, 2, abi.encodePacked((_element))); } - /** Push an element to staticU128 */ + /** Update an element of staticU128 at `_index` */ function updateStaticU128(bytes32 key, uint256 _index, uint128 _element) internal { bytes32[] memory _primaryKeys = new bytes32[](1); _primaryKeys[0] = bytes32((key)); @@ -203,7 +203,7 @@ library Dynamics { StoreSwitch.pushToField(_tableId, _primaryKeys, 3, abi.encodePacked((_element))); } - /** Push an element to staticAddrs */ + /** Update an element of staticAddrs at `_index` */ function updateStaticAddrs(bytes32 key, uint256 _index, address _element) internal { bytes32[] memory _primaryKeys = new bytes32[](1); _primaryKeys[0] = bytes32((key)); @@ -236,7 +236,7 @@ library Dynamics { StoreSwitch.pushToField(_tableId, _primaryKeys, 4, abi.encodePacked((_element))); } - /** Push an element to staticBools */ + /** Update an element of staticBools at `_index` */ function updateStaticBools(bytes32 key, uint256 _index, bool _element) internal { bytes32[] memory _primaryKeys = new bytes32[](1); _primaryKeys[0] = bytes32((key)); @@ -269,7 +269,7 @@ library Dynamics { StoreSwitch.pushToField(_tableId, _primaryKeys, 5, abi.encodePacked((_element))); } - /** Push an element to u64 */ + /** Update an element of u64 at `_index` */ function updateU64(bytes32 key, uint256 _index, uint64 _element) internal { bytes32[] memory _primaryKeys = new bytes32[](1); _primaryKeys[0] = bytes32((key)); @@ -302,7 +302,7 @@ library Dynamics { StoreSwitch.pushToField(_tableId, _primaryKeys, 6, bytes((_slice))); } - /** Push a slice to str */ + /** Update a slice of str at `_index` */ function updateStr(bytes32 key, uint256 _index, string memory _slice) internal { bytes32[] memory _primaryKeys = new bytes32[](1); _primaryKeys[0] = bytes32((key)); @@ -335,7 +335,7 @@ library Dynamics { StoreSwitch.pushToField(_tableId, _primaryKeys, 7, bytes((_slice))); } - /** Push a slice to b */ + /** Update a slice of b at `_index` */ function updateB(bytes32 key, uint256 _index, bytes memory _slice) internal { bytes32[] memory _primaryKeys = new bytes32[](1); _primaryKeys[0] = bytes32((key)); diff --git a/packages/cli/contracts/src/tables/Singleton.sol b/packages/cli/contracts/src/tables/Singleton.sol index 231d764ddf..fde19a8375 100644 --- a/packages/cli/contracts/src/tables/Singleton.sol +++ b/packages/cli/contracts/src/tables/Singleton.sol @@ -96,7 +96,7 @@ library Singleton { StoreSwitch.pushToField(_tableId, _primaryKeys, 1, abi.encodePacked((_element))); } - /** Push an element to v2 */ + /** Update an element of v2 at `_index` */ function updateV2(uint256 _index, uint32 _element) internal { bytes32[] memory _primaryKeys = new bytes32[](0); @@ -125,7 +125,7 @@ library Singleton { StoreSwitch.pushToField(_tableId, _primaryKeys, 2, abi.encodePacked((_element))); } - /** Push an element to v3 */ + /** Update an element of v3 at `_index` */ function updateV3(uint256 _index, uint32 _element) internal { bytes32[] memory _primaryKeys = new bytes32[](0); @@ -154,7 +154,7 @@ library Singleton { StoreSwitch.pushToField(_tableId, _primaryKeys, 3, abi.encodePacked((_element))); } - /** Push an element to v4 */ + /** Update an element of v4 at `_index` */ function updateV4(uint256 _index, uint32 _element) internal { bytes32[] memory _primaryKeys = new bytes32[](0); diff --git a/packages/store/src/tables/Callbacks.sol b/packages/store/src/tables/Callbacks.sol index 86a9e884e1..7c2b73c094 100644 --- a/packages/store/src/tables/Callbacks.sol +++ b/packages/store/src/tables/Callbacks.sol @@ -79,7 +79,7 @@ library Callbacks { StoreSwitch.pushToField(_tableId, _primaryKeys, 0, abi.encodePacked((_element))); } - /** Push an element to value */ + /** Update an element of value at `_index` */ function update(bytes32 key, uint256 _index, bytes24 _element) internal { bytes32[] memory _primaryKeys = new bytes32[](1); _primaryKeys[0] = bytes32((key)); diff --git a/packages/store/src/tables/Hooks.sol b/packages/store/src/tables/Hooks.sol index 7e437de213..2619baa9ff 100644 --- a/packages/store/src/tables/Hooks.sol +++ b/packages/store/src/tables/Hooks.sol @@ -79,7 +79,7 @@ library Hooks { StoreSwitch.pushToField(_tableId, _primaryKeys, 0, abi.encodePacked((_element))); } - /** Push an element to value */ + /** Update an element of value at `_index` */ function update(bytes32 key, uint256 _index, address _element) internal { bytes32[] memory _primaryKeys = new bytes32[](1); _primaryKeys[0] = bytes32((key)); diff --git a/packages/store/src/tables/Mixed.sol b/packages/store/src/tables/Mixed.sol index 084de97b95..1e5433640b 100644 --- a/packages/store/src/tables/Mixed.sol +++ b/packages/store/src/tables/Mixed.sol @@ -126,7 +126,7 @@ library Mixed { StoreSwitch.pushToField(_tableId, _primaryKeys, 2, abi.encodePacked((_element))); } - /** Push an element to a32 */ + /** Update an element of a32 at `_index` */ function updateA32(bytes32 key, uint256 _index, uint32 _element) internal { bytes32[] memory _primaryKeys = new bytes32[](1); _primaryKeys[0] = bytes32((key)); @@ -159,7 +159,7 @@ library Mixed { StoreSwitch.pushToField(_tableId, _primaryKeys, 3, bytes((_slice))); } - /** Push a slice to s */ + /** Update a slice of s at `_index` */ function updateS(bytes32 key, uint256 _index, string memory _slice) internal { bytes32[] memory _primaryKeys = new bytes32[](1); _primaryKeys[0] = bytes32((key)); diff --git a/packages/store/src/tables/StoreMetadata.sol b/packages/store/src/tables/StoreMetadata.sol index 65a87e06d5..f3f8dea97f 100644 --- a/packages/store/src/tables/StoreMetadata.sol +++ b/packages/store/src/tables/StoreMetadata.sol @@ -122,7 +122,7 @@ library StoreMetadata { _store.pushToField(_tableId, _primaryKeys, 0, bytes((_slice))); } - /** Push a slice to tableName */ + /** Update a slice of tableName at `_index` */ function updateTableName(uint256 tableId, uint256 _index, string memory _slice) internal { bytes32[] memory _primaryKeys = new bytes32[](1); _primaryKeys[0] = bytes32(uint256((tableId))); @@ -130,7 +130,7 @@ library StoreMetadata { StoreSwitch.updateInField(_tableId, _primaryKeys, 0, _index * 1, bytes((_slice))); } - /** Push a slice to tableName (using the specified store) */ + /** Update a slice of tableName (using the specified store) at `_index` */ function updateTableName(IStore _store, uint256 tableId, uint256 _index, string memory _slice) internal { bytes32[] memory _primaryKeys = new bytes32[](1); _primaryKeys[0] = bytes32(uint256((tableId))); @@ -191,7 +191,7 @@ library StoreMetadata { _store.pushToField(_tableId, _primaryKeys, 1, bytes((_slice))); } - /** Push a slice to abiEncodedFieldNames */ + /** Update a slice of abiEncodedFieldNames at `_index` */ function updateAbiEncodedFieldNames(uint256 tableId, uint256 _index, bytes memory _slice) internal { bytes32[] memory _primaryKeys = new bytes32[](1); _primaryKeys[0] = bytes32(uint256((tableId))); @@ -199,7 +199,7 @@ library StoreMetadata { StoreSwitch.updateInField(_tableId, _primaryKeys, 1, _index * 1, bytes((_slice))); } - /** Push a slice to abiEncodedFieldNames (using the specified store) */ + /** Update a slice of abiEncodedFieldNames (using the specified store) at `_index` */ function updateAbiEncodedFieldNames(IStore _store, uint256 tableId, uint256 _index, bytes memory _slice) internal { bytes32[] memory _primaryKeys = new bytes32[](1); _primaryKeys[0] = bytes32(uint256((tableId))); diff --git a/packages/world/src/modules/keyswithvalue/tables/KeysWithValue.sol b/packages/world/src/modules/keyswithvalue/tables/KeysWithValue.sol index bdbb3e6881..834fd0e901 100644 --- a/packages/world/src/modules/keyswithvalue/tables/KeysWithValue.sol +++ b/packages/world/src/modules/keyswithvalue/tables/KeysWithValue.sol @@ -116,7 +116,7 @@ library KeysWithValue { _store.pushToField(_tableId, _primaryKeys, 0, abi.encodePacked((_element))); } - /** Push an element to keysWithValue */ + /** Update an element of keysWithValue at `_index` */ function update(uint256 _tableId, bytes32 valueHash, uint256 _index, bytes32 _element) internal { bytes32[] memory _primaryKeys = new bytes32[](1); _primaryKeys[0] = bytes32((valueHash)); @@ -124,7 +124,7 @@ library KeysWithValue { StoreSwitch.updateInField(_tableId, _primaryKeys, 0, _index * 32, abi.encodePacked((_element))); } - /** Push an element to keysWithValue (using the specified store) */ + /** Update an element of keysWithValue (using the specified store) at `_index` */ function update(IStore _store, uint256 _tableId, bytes32 valueHash, uint256 _index, bytes32 _element) internal { bytes32[] memory _primaryKeys = new bytes32[](1); _primaryKeys[0] = bytes32((valueHash)); diff --git a/packages/world/src/tables/AddressArray.sol b/packages/world/src/tables/AddressArray.sol index 915400de98..f083342565 100644 --- a/packages/world/src/tables/AddressArray.sol +++ b/packages/world/src/tables/AddressArray.sol @@ -112,7 +112,7 @@ library AddressArray { _store.pushToField(_tableId, _primaryKeys, 0, abi.encodePacked((_element))); } - /** Push an element to value */ + /** Update an element of value at `_index` */ function update(uint256 _tableId, bytes32 key, uint256 _index, address _element) internal { bytes32[] memory _primaryKeys = new bytes32[](1); _primaryKeys[0] = bytes32((key)); @@ -120,7 +120,7 @@ library AddressArray { StoreSwitch.updateInField(_tableId, _primaryKeys, 0, _index * 20, abi.encodePacked((_element))); } - /** Push an element to value (using the specified store) */ + /** Update an element of value (using the specified store) at `_index` */ function update(IStore _store, uint256 _tableId, bytes32 key, uint256 _index, address _element) internal { bytes32[] memory _primaryKeys = new bytes32[](1); _primaryKeys[0] = bytes32((key)); From 0fdde551fde4f33de3342d4b12f065ae9743fd1f Mon Sep 17 00:00:00 2001 From: dk1a Date: Tue, 28 Mar 2023 17:46:15 +0300 Subject: [PATCH 6/9] chore: gas-report --- packages/store/gas-report.txt | 22 ++++++++++++---------- packages/world/gas-report.txt | 26 +++++++++++++------------- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/packages/store/gas-report.txt b/packages/store/gas-report.txt index 0bc2e43c46..463bb2dce1 100644 --- a/packages/store/gas-report.txt +++ b/packages/store/gas-report.txt @@ -15,9 +15,9 @@ (test/Gas.t.sol) | pass abi encoded bytes to external contract [someContract.doSomethingWithBytes(abiEncoded)]: 6554 (test/Gas.t.sol) | pass custom encoded bytes to external contract [someContract.doSomethingWithBytes(customEncoded)]: 1381 (test/Mixed.t.sol) | store Mixed struct in storage (native solidity) [testMixed = mixed]: 92050 -(test/Mixed.t.sol) | register Mixed schema [Mixed.registerSchema()]: 61173 -(test/Mixed.t.sol) | set record in Mixed [Mixed.set({ key: key, u32: 1, u128: 2, a32: a32, s: s })]: 112013 -(test/Mixed.t.sol) | get record from Mixed [MixedData memory mixed = Mixed.get(key)]: 13374 +(test/Mixed.t.sol) | register Mixed schema [Mixed.registerSchema()]: 61195 +(test/Mixed.t.sol) | set record in Mixed [Mixed.set({ key: key, u32: 1, u128: 2, a32: a32, s: s })]: 112057 +(test/Mixed.t.sol) | get record from Mixed [MixedData memory mixed = Mixed.get(key)]: 13396 (test/PackedCounter.t.sol) | get value at index of PackedCounter [packedCounter.atIndex(3)]: 261 (test/PackedCounter.t.sol) | set value at index of PackedCounter [packedCounter = packedCounter.setAtIndex(2, 5)]: 799 (test/PackedCounter.t.sol) | pack uint16 array into PackedCounter [PackedCounter packedCounter = PackedCounterLib.pack(counters)]: 2152 @@ -51,16 +51,16 @@ (test/StoreCore.t.sol) | delete record (complex data, 3 slots) [StoreCore.deleteRecord(table, key)]: 10963 (test/StoreCore.t.sol) | Check for existence of table (existent) [StoreCore.hasTable(table)]: 1117 (test/StoreCore.t.sol) | check for existence of table (non-existent) [StoreCore.hasTable(table2)]: 3143 -(test/StoreCore.t.sol) | register subscriber [StoreCore.registerStoreHook(table, subscriber)]: 65439 +(test/StoreCore.t.sol) | register subscriber [StoreCore.registerStoreHook(table, subscriber)]: 65488 (test/StoreCore.t.sol) | set record on table with subscriber [StoreCore.setRecord(table, key, data)]: 73772 (test/StoreCore.t.sol) | set static field on table with subscriber [StoreCore.setField(table, key, 0, data)]: 31929 (test/StoreCore.t.sol) | delete record on table with subscriber [StoreCore.deleteRecord(table, key)]: 24371 -(test/StoreCore.t.sol) | register subscriber [StoreCore.registerStoreHook(table, subscriber)]: 65439 +(test/StoreCore.t.sol) | register subscriber [StoreCore.registerStoreHook(table, subscriber)]: 65488 (test/StoreCore.t.sol) | set (dynamic) record on table with subscriber [StoreCore.setRecord(table, key, data)]: 167179 (test/StoreCore.t.sol) | set (dynamic) field on table with subscriber [StoreCore.setField(table, key, 1, arrayDataBytes)]: 35031 (test/StoreCore.t.sol) | delete (dynamic) record on table with subscriber [StoreCore.deleteRecord(table, key)]: 25851 -(test/StoreCore.t.sol) | push to field (1 slot, 1 uint32 item) [StoreCore.pushToField(table, key, 1, secondDataToPush)]: 16958 -(test/StoreCore.t.sol) | push to field (2 slots, 10 uint32 items) [StoreCore.pushToField(table, key, 2, thirdDataToPush)]: 39680 +(test/StoreCore.t.sol) | push to field (1 slot, 1 uint32 item) [StoreCore.pushToField(table, key, 1, secondDataToPush)]: 17007 +(test/StoreCore.t.sol) | push to field (2 slots, 10 uint32 items) [StoreCore.pushToField(table, key, 2, thirdDataToPush)]: 39729 (test/StoreCore.t.sol) | StoreCore: register schema [StoreCore.registerSchema(table, schema, keySchema)]: 54866 (test/StoreCore.t.sol) | StoreCore: get schema (warm) [Schema loadedSchema = StoreCore.getSchema(table)]: 1145 (test/StoreCore.t.sol) | StoreCore: get key schema (warm) [Schema loadedKeySchema = StoreCore.getKeySchema(table)]: 1213 @@ -83,9 +83,11 @@ (test/StoreCore.t.sol) | get static record (1 slot) [bytes memory loadedData = StoreCore.getRecord(table, key, schema)]: 1335 (test/StoreCore.t.sol) | set static record (2 slots) [StoreCore.setRecord(table, key, data)]: 59833 (test/StoreCore.t.sol) | get static record (2 slots) [bytes memory loadedData = StoreCore.getRecord(table, key, schema)]: 1580 -(test/StoreCore.t.sol) | StoreCore: set table metadata [StoreCore.setMetadata(table, tableName, fieldNames)]: 251710 -(test/StoreMetadata.t.sol) | set record in StoreMetadataTable [StoreMetadata.set({ tableId: tableId, tableName: tableName, abiEncodedFieldNames: abi.encode(fieldNames) })]: 250184 -(test/StoreMetadata.t.sol) | get record from StoreMetadataTable [StoreMetadataData memory metadata = StoreMetadata.get(tableId)]: 12110 +(test/StoreCore.t.sol) | StoreCore: set table metadata [StoreCore.setMetadata(table, tableName, fieldNames)]: 251702 +(test/StoreCore.t.sol) | update in field (1 slot, 1 uint32 item) [StoreCore.updateInField(table, key, 1, 4 * 1, secondDataForUpdate)]: 16544 +(test/StoreCore.t.sol) | push to field (2 slots, 6 uint64 items) [StoreCore.updateInField(table, key, 2, 8 * 1, thirdDataForUpdate)]: 17646 +(test/StoreMetadata.t.sol) | set record in StoreMetadataTable [StoreMetadata.set({ tableId: tableId, tableName: tableName, abiEncodedFieldNames: abi.encode(fieldNames) })]: 250220 +(test/StoreMetadata.t.sol) | get record from StoreMetadataTable [StoreMetadataData memory metadata = StoreMetadata.get(tableId)]: 12132 (test/StoreSwitch.t.sol) | check if delegatecall [isDelegate = StoreSwitch.isDelegateCall()]: 671 (test/StoreSwitch.t.sol) | check if delegatecall [isDelegate = StoreSwitch.isDelegateCall()]: 627 (test/Vector2.t.sol) | register Vector2 schema [Vector2.registerSchema()]: 57971 diff --git a/packages/world/gas-report.txt b/packages/world/gas-report.txt index fe18f59c1d..99cd408061 100644 --- a/packages/world/gas-report.txt +++ b/packages/world/gas-report.txt @@ -1,16 +1,16 @@ -(test/KeysWithValueModule.t.sol) | install keys with value module [world.installRootModule(keysWithValueModule, abi.encode(sourceTableId))]: 600319 -(test/KeysWithValueModule.t.sol) | Get list of keys with a given value [bytes32[] memory keysWithValue = getKeysWithValue(world, sourceTableId, abi.encode(value1))]: 7672 +(test/KeysWithValueModule.t.sol) | install keys with value module [world.installRootModule(keysWithValueModule, abi.encode(sourceTableId))]: 600412 +(test/KeysWithValueModule.t.sol) | Get list of keys with a given value [bytes32[] memory keysWithValue = getKeysWithValue(world, sourceTableId, abi.encode(value1))]: 7694 (test/KeysWithValueModule.t.sol) | compute the target table selector [bytes32 targetTableSelector = getTargetTableSelector(sourceTableId)]: 2245 -(test/KeysWithValueModule.t.sol) | install keys with value module [world.installRootModule(keysWithValueModule, abi.encode(sourceTableId))]: 600319 -(test/KeysWithValueModule.t.sol) | set a record on a table with KeysWithValueModule installed [world.setRecord(namespace, sourceFile, keyTuple1, abi.encodePacked(value))]: 169477 -(test/KeysWithValueModule.t.sol) | install keys with value module [world.installRootModule(keysWithValueModule, abi.encode(sourceTableId))]: 600319 -(test/KeysWithValueModule.t.sol) | change a record on a table with KeysWithValueModule installed [world.setRecord(namespace, sourceFile, keyTuple1, abi.encodePacked(value2))]: 135475 -(test/KeysWithValueModule.t.sol) | delete a record on a table with KeysWithValueModule installed [world.deleteRecord(namespace, sourceFile, keyTuple1)]: 57998 -(test/KeysWithValueModule.t.sol) | install keys with value module [world.installRootModule(keysWithValueModule, abi.encode(sourceTableId))]: 600319 -(test/KeysWithValueModule.t.sol) | set a field on a table with KeysWithValueModule installed [world.setField(namespace, sourceFile, keyTuple1, 0, abi.encodePacked(value1))]: 177731 -(test/KeysWithValueModule.t.sol) | change a field on a table with KeysWithValueModule installed [world.setField(namespace, sourceFile, keyTuple1, 0, abi.encodePacked(value2))]: 142076 -(test/World.t.sol) | Delete record [world.deleteRecord(namespace, file, singletonKey)]: 16115 -(test/World.t.sol) | Push data to the table [world.pushToField(namespace, file, keyTuple, 0, encodedData)]: 96477 +(test/KeysWithValueModule.t.sol) | install keys with value module [world.installRootModule(keysWithValueModule, abi.encode(sourceTableId))]: 600412 +(test/KeysWithValueModule.t.sol) | set a record on a table with KeysWithValueModule installed [world.setRecord(namespace, sourceFile, keyTuple1, abi.encodePacked(value))]: 169503 +(test/KeysWithValueModule.t.sol) | install keys with value module [world.installRootModule(keysWithValueModule, abi.encode(sourceTableId))]: 600412 +(test/KeysWithValueModule.t.sol) | change a record on a table with KeysWithValueModule installed [world.setRecord(namespace, sourceFile, keyTuple1, abi.encodePacked(value2))]: 135501 +(test/KeysWithValueModule.t.sol) | delete a record on a table with KeysWithValueModule installed [world.deleteRecord(namespace, sourceFile, keyTuple1)]: 57997 +(test/KeysWithValueModule.t.sol) | install keys with value module [world.installRootModule(keysWithValueModule, abi.encode(sourceTableId))]: 600412 +(test/KeysWithValueModule.t.sol) | set a field on a table with KeysWithValueModule installed [world.setField(namespace, sourceFile, keyTuple1, 0, abi.encodePacked(value1))]: 177712 +(test/KeysWithValueModule.t.sol) | change a field on a table with KeysWithValueModule installed [world.setField(namespace, sourceFile, keyTuple1, 0, abi.encodePacked(value2))]: 142057 +(test/World.t.sol) | Delete record [world.deleteRecord(namespace, file, singletonKey)]: 16137 +(test/World.t.sol) | Push data to the table [world.pushToField(namespace, file, keyTuple, 0, encodedData)]: 96526 (test/World.t.sol) | Register a fallback system [bytes4 funcSelector1 = world.registerFunctionSelector(namespace, file, "", "")]: 81251 (test/World.t.sol) | Register a root fallback system [bytes4 funcSelector2 = world.registerRootFunctionSelector(namespace, file, worldFunc, 0)]: 72478 (test/World.t.sol) | Register a function selector [bytes4 functionSelector = world.registerFunctionSelector(namespace, file, "msgSender", "()")]: 101848 @@ -18,5 +18,5 @@ (test/World.t.sol) | Register a root function selector [bytes4 functionSelector = world.registerRootFunctionSelector(namespace, file, worldFunc, sysFunc)]: 96384 (test/World.t.sol) | Register a new table in the namespace [bytes32 tableSelector = world.registerTable(namespace, table, schema, defaultKeySchema)]: 252912 (test/World.t.sol) | Write data to a table field [world.setField(namespace, file, singletonKey, 0, abi.encodePacked(true))]: 44816 -(test/World.t.sol) | Set metadata [world.setMetadata(namespace, file, tableName, fieldNames)]: 277464 +(test/World.t.sol) | Set metadata [world.setMetadata(namespace, file, tableName, fieldNames)]: 277486 (test/World.t.sol) | Write data to the table [Bool.set(world, tableId, true)]: 42697 \ No newline at end of file From 5c45966d4e715eff100a58f56b3c9248e2a7215a Mon Sep 17 00:00:00 2001 From: dk1a Date: Tue, 28 Mar 2023 20:00:37 +0300 Subject: [PATCH 7/9] refactor: rename startIndex to startByteIndex --- packages/store/src/IStore.sol | 2 +- packages/store/src/StoreCore.sol | 22 ++++++++++---------- packages/store/src/StoreSwitch.sol | 6 +++--- packages/store/test/StoreCore.t.sol | 6 +++--- packages/world/src/World.sol | 19 +++++++++++------ packages/world/src/interfaces/IWorldCore.sol | 4 ++-- 6 files changed, 33 insertions(+), 26 deletions(-) diff --git a/packages/store/src/IStore.sol b/packages/store/src/IStore.sol index ee3b113452..a05a25577e 100644 --- a/packages/store/src/IStore.sol +++ b/packages/store/src/IStore.sol @@ -33,7 +33,7 @@ interface IStore { uint256 table, bytes32[] calldata key, uint8 schemaIndex, - uint256 startIndex, + uint256 startByteIndex, bytes calldata dataToSet ) external; diff --git a/packages/store/src/StoreCore.sol b/packages/store/src/StoreCore.sol index fc11750d38..bbabfe823d 100644 --- a/packages/store/src/StoreCore.sol +++ b/packages/store/src/StoreCore.sol @@ -305,7 +305,7 @@ library StoreCore { uint256 tableId, bytes32[] memory key, uint8 schemaIndex, - uint256 startIndex, + uint256 startByteIndex, bytes memory dataToSet ) internal { Schema schema = getSchema(tableId); @@ -315,8 +315,8 @@ library StoreCore { } // index must be checked because it could be arbitrarily large // (but dataToSet.length can be unchecked - it won't overflow into another slot due to gas costs and hashed slots) - if (startIndex > type(uint16).max) { - revert StoreCore_DataIndexOverflow(type(uint16).max, startIndex); + if (startByteIndex > type(uint16).max) { + revert StoreCore_DataIndexOverflow(type(uint16).max, startByteIndex); } // TODO add setItem-specific event and hook to avoid the storage read? (https://github.com/latticexyz/mud/issues/444) @@ -324,9 +324,9 @@ library StoreCore { { bytes memory oldData = StoreCoreInternal._getDynamicField(tableId, key, schemaIndex, schema); fullData = abi.encodePacked( - SliceLib.getSubslice(oldData, 0, startIndex).toBytes(), + SliceLib.getSubslice(oldData, 0, startByteIndex).toBytes(), dataToSet, - SliceLib.getSubslice(oldData, startIndex + dataToSet.length, oldData.length).toBytes() + SliceLib.getSubslice(oldData, startByteIndex + dataToSet.length, oldData.length).toBytes() ); } @@ -340,7 +340,7 @@ library StoreCore { hook.onBeforeSetField(tableId, key, schemaIndex, fullData); } - StoreCoreInternal._setDynamicFieldItem(tableId, key, schema, schemaIndex, startIndex, dataToSet); + StoreCoreInternal._setDynamicFieldItem(tableId, key, schema, schemaIndex, startByteIndex, dataToSet); // Call onAfterSetField hooks (after modifying the state) for (uint256 i; i < hooks.length; i++) { @@ -547,13 +547,13 @@ library StoreCoreInternal { bytes32[] memory key, Schema schema, uint8 schemaIndex, - uint256 startIndex, + uint256 startByteIndex, bytes memory dataToSet ) internal { uint8 dynamicSchemaIndex = schemaIndex - schema.numStaticFields(); // Set `dataToSet` at the given index - _setPartialDynamicData(tableId, key, dynamicSchemaIndex, startIndex, dataToSet); + _setPartialDynamicData(tableId, key, dynamicSchemaIndex, startByteIndex, dataToSet); } /************************************************************************ @@ -704,14 +704,14 @@ library StoreCoreInternal { uint256 tableId, bytes32[] memory key, uint8 dynamicSchemaIndex, - uint256 startIndex, + uint256 startByteIndex, bytes memory partialData ) internal { uint256 dynamicDataLocation = _getDynamicDataLocation(tableId, key, dynamicSchemaIndex); // start index is in bytes, whereas storage slots are in 32-byte words - dynamicDataLocation += startIndex / 32; + dynamicDataLocation += startByteIndex / 32; // partial storage slot offset (there is no inherent offset, as each dynamic field starts at its own storage slot) - uint256 offset = startIndex % 32; + uint256 offset = startByteIndex % 32; Storage.store({ storagePointer: dynamicDataLocation, offset: offset, data: partialData }); } } diff --git a/packages/store/src/StoreSwitch.sol b/packages/store/src/StoreSwitch.sol index de7f626659..7945a3f4e3 100644 --- a/packages/store/src/StoreSwitch.sol +++ b/packages/store/src/StoreSwitch.sol @@ -94,13 +94,13 @@ library StoreSwitch { uint256 table, bytes32[] memory key, uint8 fieldIndex, - uint256 startIndex, + uint256 startByteIndex, bytes memory dataToSet ) internal { if (isDelegateCall()) { - StoreCore.updateInField(table, key, fieldIndex, startIndex, dataToSet); + StoreCore.updateInField(table, key, fieldIndex, startByteIndex, dataToSet); } else { - IStore(msg.sender).updateInField(table, key, fieldIndex, startIndex, dataToSet); + IStore(msg.sender).updateInField(table, key, fieldIndex, startByteIndex, dataToSet); } } diff --git a/packages/store/test/StoreCore.t.sol b/packages/store/test/StoreCore.t.sol index 752866b9fa..02ddd6354a 100644 --- a/packages/store/test/StoreCore.t.sol +++ b/packages/store/test/StoreCore.t.sol @@ -51,10 +51,10 @@ contract StoreCoreTest is Test, StoreView { uint256 table, bytes32[] calldata key, uint8 schemaIndex, - uint256 startIndex, + uint256 startByteIndex, bytes calldata dataToSet ) public override { - StoreCore.updateInField(table, key, schemaIndex, startIndex, dataToSet); + StoreCore.updateInField(table, key, schemaIndex, startByteIndex, dataToSet); } // Expose an external deleteRecord function for testing purposes of indexers (see testHooks) @@ -774,7 +774,7 @@ contract StoreCoreTest is Test, StoreView { assertEq(bytes32(StoreCore.getField(table, key, 0)), firstDataBytes); assertEq(StoreCore.getField(table, key, 1), newSecondDataBytes); - // startIndex must not overflow + // startByteIndex must not overflow vm.expectRevert( abi.encodeWithSelector(StoreCore.StoreCore_DataIndexOverflow.selector, type(uint16).max, type(uint32).max) ); diff --git a/packages/world/src/World.sol b/packages/world/src/World.sol index aa9a4153c5..66fa557eaa 100644 --- a/packages/world/src/World.sol +++ b/packages/world/src/World.sol @@ -164,7 +164,7 @@ contract World is Store, IWorldCore, IErrors { } /** - * Update data at `startIndex` of a field in the table at the given namespace and file. + * Update data at `startByteIndex` of a field in the table at the given namespace and file. * Requires the caller to have access to the namespace or file. */ function updateInField( @@ -172,14 +172,14 @@ contract World is Store, IWorldCore, IErrors { bytes16 file, bytes32[] calldata key, uint8 schemaIndex, - uint256 startIndex, + uint256 startByteIndex, bytes calldata dataToSet ) public virtual { // Require access to namespace or file bytes32 resourceSelector = AccessControl.requireAccess(namespace, file, msg.sender); // Update data in the field - StoreCore.updateInField(resourceSelector.toTableId(), key, schemaIndex, startIndex, dataToSet); + StoreCore.updateInField(resourceSelector.toTableId(), key, schemaIndex, startByteIndex, dataToSet); } /** @@ -314,7 +314,7 @@ contract World is Store, IWorldCore, IErrors { } /** - * Update data at `startIndex` of a field in the table at the given tableId. + * Update data at `startByteIndex` of a field in the table at the given tableId. * This overload exists to conform with the `IStore` interface. * The tableId is converted to a resourceSelector, and access is checked based on the namespace or file. */ @@ -322,11 +322,18 @@ contract World is Store, IWorldCore, IErrors { uint256 tableId, bytes32[] calldata key, uint8 schemaIndex, - uint256 startIndex, + uint256 startByteIndex, bytes calldata dataToSet ) public virtual { bytes32 resourceSelector = ResourceSelector.from(tableId); - updateInField(resourceSelector.getNamespace(), resourceSelector.getFile(), key, schemaIndex, startIndex, dataToSet); + updateInField( + resourceSelector.getNamespace(), + resourceSelector.getFile(), + key, + schemaIndex, + startByteIndex, + dataToSet + ); } /** diff --git a/packages/world/src/interfaces/IWorldCore.sol b/packages/world/src/interfaces/IWorldCore.sol index e01a0a12ab..0e49f38256 100644 --- a/packages/world/src/interfaces/IWorldCore.sol +++ b/packages/world/src/interfaces/IWorldCore.sol @@ -84,7 +84,7 @@ interface IWorldCore { ) external; /** - * Update data at `startIndex` of a field in the table at the given namespace and file. + * Update data at `startByteIndex` of a field in the table at the given namespace and file. * Requires the caller to have access to the namespace or file. */ function updateInField( @@ -92,7 +92,7 @@ interface IWorldCore { bytes16 file, bytes32[] calldata key, uint8 schemaIndex, - uint256 startIndex, + uint256 startByteIndex, bytes calldata dataToSet ) external; From 38597b17748dbbed15867f45f7a1431e1d2c5d25 Mon Sep 17 00:00:00 2001 From: dk1a Date: Fri, 31 Mar 2023 19:24:20 +0300 Subject: [PATCH 8/9] chore: gas-report --- packages/world/gas-report.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/world/gas-report.txt b/packages/world/gas-report.txt index 99cd408061..8004de9d24 100644 --- a/packages/world/gas-report.txt +++ b/packages/world/gas-report.txt @@ -18,5 +18,5 @@ (test/World.t.sol) | Register a root function selector [bytes4 functionSelector = world.registerRootFunctionSelector(namespace, file, worldFunc, sysFunc)]: 96384 (test/World.t.sol) | Register a new table in the namespace [bytes32 tableSelector = world.registerTable(namespace, table, schema, defaultKeySchema)]: 252912 (test/World.t.sol) | Write data to a table field [world.setField(namespace, file, singletonKey, 0, abi.encodePacked(true))]: 44816 -(test/World.t.sol) | Set metadata [world.setMetadata(namespace, file, tableName, fieldNames)]: 277486 +(test/World.t.sol) | Set metadata [world.setMetadata(namespace, file, tableName, fieldNames)]: 277470 (test/World.t.sol) | Write data to the table [Bool.set(world, tableId, true)]: 42697 \ No newline at end of file From da1e2a545a793f7e004f0b8f87864ac38c017794 Mon Sep 17 00:00:00 2001 From: dk1a Date: Tue, 4 Apr 2023 13:27:18 +0300 Subject: [PATCH 9/9] chore(store): tablegen --- packages/store/src/tables/Callbacks.sol | 16 +++++++++++ packages/store/src/tables/Hooks.sol | 16 +++++++++++ packages/store/src/tables/Mixed.sol | 32 +++++++++++++++++++++ packages/store/src/tables/StoreMetadata.sol | 32 +++++++++++++++++++++ 4 files changed, 96 insertions(+) diff --git a/packages/store/src/tables/Callbacks.sol b/packages/store/src/tables/Callbacks.sol index d350f863d5..288e91f34f 100644 --- a/packages/store/src/tables/Callbacks.sol +++ b/packages/store/src/tables/Callbacks.sol @@ -115,6 +115,22 @@ library Callbacks { _store.pushToField(_tableId, _primaryKeys, 0, abi.encodePacked((_element))); } + /** Update an element of value at `_index` */ + function update(bytes32 key, uint256 _index, bytes24 _element) internal { + bytes32[] memory _primaryKeys = new bytes32[](1); + _primaryKeys[0] = bytes32((key)); + + StoreSwitch.updateInField(_tableId, _primaryKeys, 0, _index * 24, abi.encodePacked((_element))); + } + + /** Update an element of value (using the specified store) at `_index` */ + function update(IStore _store, bytes32 key, uint256 _index, bytes24 _element) internal { + bytes32[] memory _primaryKeys = new bytes32[](1); + _primaryKeys[0] = bytes32((key)); + + _store.updateInField(_tableId, _primaryKeys, 0, _index * 24, abi.encodePacked((_element))); + } + /** Tightly pack full data using this table's schema */ function encode(bytes24[] memory value) internal view returns (bytes memory) { uint16[] memory _counters = new uint16[](1); diff --git a/packages/store/src/tables/Hooks.sol b/packages/store/src/tables/Hooks.sol index e37769f7c5..f95ef9ba96 100644 --- a/packages/store/src/tables/Hooks.sol +++ b/packages/store/src/tables/Hooks.sol @@ -115,6 +115,22 @@ library Hooks { _store.pushToField(_tableId, _primaryKeys, 0, abi.encodePacked((_element))); } + /** Update an element of value at `_index` */ + function update(bytes32 key, uint256 _index, address _element) internal { + bytes32[] memory _primaryKeys = new bytes32[](1); + _primaryKeys[0] = bytes32((key)); + + StoreSwitch.updateInField(_tableId, _primaryKeys, 0, _index * 20, abi.encodePacked((_element))); + } + + /** Update an element of value (using the specified store) at `_index` */ + function update(IStore _store, bytes32 key, uint256 _index, address _element) internal { + bytes32[] memory _primaryKeys = new bytes32[](1); + _primaryKeys[0] = bytes32((key)); + + _store.updateInField(_tableId, _primaryKeys, 0, _index * 20, abi.encodePacked((_element))); + } + /** Tightly pack full data using this table's schema */ function encode(address[] memory value) internal view returns (bytes memory) { uint16[] memory _counters = new uint16[](1); diff --git a/packages/store/src/tables/Mixed.sol b/packages/store/src/tables/Mixed.sol index edd7d42507..c46666f239 100644 --- a/packages/store/src/tables/Mixed.sol +++ b/packages/store/src/tables/Mixed.sol @@ -196,6 +196,22 @@ library Mixed { _store.pushToField(_tableId, _primaryKeys, 2, abi.encodePacked((_element))); } + /** Update an element of a32 at `_index` */ + function updateA32(bytes32 key, uint256 _index, uint32 _element) internal { + bytes32[] memory _primaryKeys = new bytes32[](1); + _primaryKeys[0] = bytes32((key)); + + StoreSwitch.updateInField(_tableId, _primaryKeys, 2, _index * 4, abi.encodePacked((_element))); + } + + /** Update an element of a32 (using the specified store) at `_index` */ + function updateA32(IStore _store, bytes32 key, uint256 _index, uint32 _element) internal { + bytes32[] memory _primaryKeys = new bytes32[](1); + _primaryKeys[0] = bytes32((key)); + + _store.updateInField(_tableId, _primaryKeys, 2, _index * 4, abi.encodePacked((_element))); + } + /** Get s */ function getS(bytes32 key) internal view returns (string memory s) { bytes32[] memory _primaryKeys = new bytes32[](1); @@ -246,6 +262,22 @@ library Mixed { _store.pushToField(_tableId, _primaryKeys, 3, bytes((_slice))); } + /** Update a slice of s at `_index` */ + function updateS(bytes32 key, uint256 _index, string memory _slice) internal { + bytes32[] memory _primaryKeys = new bytes32[](1); + _primaryKeys[0] = bytes32((key)); + + StoreSwitch.updateInField(_tableId, _primaryKeys, 3, _index * 1, bytes((_slice))); + } + + /** Update a slice of s (using the specified store) at `_index` */ + function updateS(IStore _store, bytes32 key, uint256 _index, string memory _slice) internal { + bytes32[] memory _primaryKeys = new bytes32[](1); + _primaryKeys[0] = bytes32((key)); + + _store.updateInField(_tableId, _primaryKeys, 3, _index * 1, bytes((_slice))); + } + /** Get the full data */ function get(bytes32 key) internal view returns (MixedData memory _table) { bytes32[] memory _primaryKeys = new bytes32[](1); diff --git a/packages/store/src/tables/StoreMetadata.sol b/packages/store/src/tables/StoreMetadata.sol index 703791e341..6d4d52b02b 100644 --- a/packages/store/src/tables/StoreMetadata.sol +++ b/packages/store/src/tables/StoreMetadata.sol @@ -122,6 +122,22 @@ library StoreMetadata { _store.pushToField(_tableId, _primaryKeys, 0, bytes((_slice))); } + /** Update a slice of tableName at `_index` */ + function updateTableName(uint256 tableId, uint256 _index, string memory _slice) internal { + bytes32[] memory _primaryKeys = new bytes32[](1); + _primaryKeys[0] = bytes32(uint256((tableId))); + + StoreSwitch.updateInField(_tableId, _primaryKeys, 0, _index * 1, bytes((_slice))); + } + + /** Update a slice of tableName (using the specified store) at `_index` */ + function updateTableName(IStore _store, uint256 tableId, uint256 _index, string memory _slice) internal { + bytes32[] memory _primaryKeys = new bytes32[](1); + _primaryKeys[0] = bytes32(uint256((tableId))); + + _store.updateInField(_tableId, _primaryKeys, 0, _index * 1, bytes((_slice))); + } + /** Get abiEncodedFieldNames */ function getAbiEncodedFieldNames(uint256 tableId) internal view returns (bytes memory abiEncodedFieldNames) { bytes32[] memory _primaryKeys = new bytes32[](1); @@ -175,6 +191,22 @@ library StoreMetadata { _store.pushToField(_tableId, _primaryKeys, 1, bytes((_slice))); } + /** Update a slice of abiEncodedFieldNames at `_index` */ + function updateAbiEncodedFieldNames(uint256 tableId, uint256 _index, bytes memory _slice) internal { + bytes32[] memory _primaryKeys = new bytes32[](1); + _primaryKeys[0] = bytes32(uint256((tableId))); + + StoreSwitch.updateInField(_tableId, _primaryKeys, 1, _index * 1, bytes((_slice))); + } + + /** Update a slice of abiEncodedFieldNames (using the specified store) at `_index` */ + function updateAbiEncodedFieldNames(IStore _store, uint256 tableId, uint256 _index, bytes memory _slice) internal { + bytes32[] memory _primaryKeys = new bytes32[](1); + _primaryKeys[0] = bytes32(uint256((tableId))); + + _store.updateInField(_tableId, _primaryKeys, 1, _index * 1, bytes((_slice))); + } + /** Get the full data */ function get(uint256 tableId) internal view returns (StoreMetadataData memory _table) { bytes32[] memory _primaryKeys = new bytes32[](1);