diff --git a/.changeset/chilled-cougars-smash.md b/.changeset/chilled-cougars-smash.md new file mode 100644 index 0000000000..0356ce2c82 --- /dev/null +++ b/.changeset/chilled-cougars-smash.md @@ -0,0 +1,23 @@ +--- +"@latticexyz/faucet": minor +--- + +New package to run your own faucet service. We'll use this soon for our testnet in place of `@latticexyz/services`. + +To run the faucet server: + +- Add the package with `pnpm add @latticexyz/faucet` +- Add a `.env` file that has a `RPC_HTTP_URL` and `FAUCET_PRIVATE_KEY` (or pass the environment variables into the next command) +- Run `pnpm faucet-server` to start the server + +You can also adjust the server's `HOST` (defaults to `0.0.0.0`) and `PORT` (defaults to `3002`). The tRPC routes are accessible under `/trpc`. + +To connect a tRPC client, add the package with `pnpm add @latticexyz/faucet` and then use `createClient`: + +```ts +import { createClient } from "@latticexyz/faucet"; + +const faucet = createClient({ url: "http://localhost:3002/trpc" }); + +await faucet.mutate.drip({ address: burnerAccount.address }); +``` diff --git a/.changeset/cyan-hats-try.md b/.changeset/cyan-hats-try.md new file mode 100644 index 0000000000..877a795bba --- /dev/null +++ b/.changeset/cyan-hats-try.md @@ -0,0 +1,30 @@ +--- +"@latticexyz/store": major +"@latticexyz/world": major +--- + +We've updated Store events to be "schemaless", meaning there is enough information in each event to only need to operate on the bytes of each record to make an update to that record without having to first decode the record by its schema. This enables new kinds of indexers and sync strategies. + +If you've written your own sync logic or are interacting with Store calls directly, this is a breaking change. We have a few more breaking protocol changes upcoming, so you may hold off on upgrading until those land. + +If you are using MUD's built-in tooling (table codegen, indexer, store sync, etc.), you don't have to make any changes except upgrading to the latest versions and deploying a fresh World. + +- The `data` field in each `StoreSetRecord` and `StoreEphemeralRecord` has been replaced with three new fields: `staticData`, `encodedLengths`, and `dynamicData`. This better reflects the on-chain state and makes it easier to perform modifications to the raw bytes. We recommend storing each of these fields individually in your off-chain storage of choice (indexer, client, etc.). + + ```diff + - event StoreSetRecord(bytes32 tableId, bytes32[] keyTuple, bytes data); + + event StoreSetRecord(bytes32 tableId, bytes32[] keyTuple, bytes staticData, bytes32 encodedLengths, bytes dynamicData); + + - event StoreEphemeralRecord(bytes32 tableId, bytes32[] keyTuple, bytes data); + + event StoreEphemeralRecord(bytes32 tableId, bytes32[] keyTuple, bytes staticData, bytes32 encodedLengths, bytes dynamicData); + ``` + +- The `StoreSetField` event is now replaced by two new events: `StoreSpliceStaticData` and `StoreSpliceDynamicData`. Splicing allows us to perform efficient operations like push and pop, in addition to replacing a field value. We use two events because updating a dynamic-length field also requires updating the record's `encodedLengths` (aka PackedCounter). + + ```diff + - event StoreSetField(bytes32 tableId, bytes32[] keyTuple, uint8 fieldIndex, bytes data); + + event StoreSpliceStaticData(bytes32 tableId, bytes32[] keyTuple, uint48 start, uint40 deleteCount, bytes data); + + event StoreSpliceDynamicData(bytes32 tableId, bytes32[] keyTuple, uint48 start, uint40 deleteCount, bytes data, bytes32 encodedLengths); + ``` + +Similarly, Store setter methods (e.g. `setRecord`) have been updated to reflect the `data` to `staticData`, `encodedLengths`, and `dynamicData` changes. We'll be following up shortly with Store getter method changes for more gas efficient storage reads. diff --git a/.changeset/fast-zebras-promise.md b/.changeset/fast-zebras-promise.md new file mode 100644 index 0000000000..f7d1cef47e --- /dev/null +++ b/.changeset/fast-zebras-promise.md @@ -0,0 +1,6 @@ +--- +"@latticexyz/common": minor +"@latticexyz/protocol-parser": major +--- + +`readHex` was moved from `@latticexyz/protocol-parser` to `@latticexyz/common` diff --git a/.changeset/hot-mice-play.md b/.changeset/hot-mice-play.md new file mode 100644 index 0000000000..defaa99a25 --- /dev/null +++ b/.changeset/hot-mice-play.md @@ -0,0 +1,8 @@ +--- +"@latticexyz/cli": patch +"@latticexyz/store": patch +"@latticexyz/world": patch +--- + +The `FieldLayout` in table libraries is now generated at compile time instead of dynamically in a table library function. +This significantly reduces gas cost in all table library functions. diff --git a/.changeset/hungry-rings-doubt.md b/.changeset/hungry-rings-doubt.md new file mode 100644 index 0000000000..d5d3ac9263 --- /dev/null +++ b/.changeset/hungry-rings-doubt.md @@ -0,0 +1,5 @@ +--- +"@latticexyz/store": patch +--- + +Added `Storage.loadField` to optimize loading 32 bytes or less from storage (which is always the case when loading data for static fields). diff --git a/.changeset/rotten-cats-lay.md b/.changeset/rotten-cats-lay.md new file mode 100644 index 0000000000..021154c423 --- /dev/null +++ b/.changeset/rotten-cats-lay.md @@ -0,0 +1,9 @@ +--- +"@latticexyz/common": minor +--- + +`spliceHex` was added, which has a similar API as JavaScript's [`Array.prototype.splice`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice), but for `Hex` strings. + +```ts +spliceHex("0x123456", 1, 1, "0x0000"); // "0x12000056" +``` diff --git a/.changeset/short-dragons-shout.md b/.changeset/short-dragons-shout.md new file mode 100644 index 0000000000..d2c8abe6f1 --- /dev/null +++ b/.changeset/short-dragons-shout.md @@ -0,0 +1,6 @@ +--- +"@latticexyz/store": patch +"@latticexyz/world": patch +--- + +Optimized the `StoreCore` hash function determining the data location to use less gas. diff --git a/.changeset/shy-sheep-wait.md b/.changeset/shy-sheep-wait.md new file mode 100644 index 0000000000..baabf2b946 --- /dev/null +++ b/.changeset/shy-sheep-wait.md @@ -0,0 +1,9 @@ +--- +"@latticexyz/dev-tools": major +"@latticexyz/store-sync": major +"create-mud": minor +--- + +We've updated Store events to be "schemaless", meaning there is enough information in each event to only need to operate on the bytes of each record to make an update to that record without having to first decode the record by its schema. This enables new kinds of indexers and sync strategies. + +As such, we've replaced `blockStorageOperations$` with `storedBlockLogs$`, a stream of simplified Store event logs after they've been synced to the configured storage adapter. These logs may not reflect exactly the events that are on chain when e.g. hydrating from an indexer, but they will still allow the client to "catch up" to the on-chain state of your tables. diff --git a/.changeset/sour-cycles-warn.md b/.changeset/sour-cycles-warn.md new file mode 100644 index 0000000000..1a79d508c5 --- /dev/null +++ b/.changeset/sour-cycles-warn.md @@ -0,0 +1,34 @@ +--- +"@latticexyz/cli": patch +"@latticexyz/store": minor +"@latticexyz/world": patch +--- + +`StoreCore` and `IStore` now expose specific functions for `getStaticField` and `getDynamicField` in addition to the general `getField`. +Using the specific functions reduces gas overhead because more optimized logic can be executed. + +```solidity +interface IStore { + /** + * Get a single static field from the given tableId and key tuple, with the given value field layout. + * Note: the field value is left-aligned in the returned bytes32, the rest of the word is not zeroed out. + * Consumers are expected to truncate the returned value as needed. + */ + function getStaticField( + bytes32 tableId, + bytes32[] calldata keyTuple, + uint8 fieldIndex, + FieldLayout fieldLayout + ) external view returns (bytes32); + + /** + * Get a single dynamic field from the given tableId and key tuple at the given dynamic field index. + * (Dynamic field index = field index - number of static fields) + */ + function getDynamicField( + bytes32 tableId, + bytes32[] memory keyTuple, + uint8 dynamicFieldIndex + ) external view returns (bytes memory); +} +``` diff --git a/.changeset/stale-worms-hunt.md b/.changeset/stale-worms-hunt.md new file mode 100644 index 0000000000..07e7c2fb16 --- /dev/null +++ b/.changeset/stale-worms-hunt.md @@ -0,0 +1,5 @@ +--- +"@latticexyz/store": major +--- + +Store events now use an `indexed` `tableId`. This adds ~100 gas per write, but means we our sync stack can filter events by table. diff --git a/.changeset/thin-terms-lay.md b/.changeset/thin-terms-lay.md new file mode 100644 index 0000000000..5f71ab8524 --- /dev/null +++ b/.changeset/thin-terms-lay.md @@ -0,0 +1,23 @@ +--- +"@latticexyz/cli": patch +"@latticexyz/common": minor +"@latticexyz/store": minor +"@latticexyz/world": patch +--- + +Generated table libraries now have a set of functions prefixed with `_` that always use their own storage for read/write. +This saves gas for use cases where the functionality to dynamically determine which `Store` to use for read/write is not needed, e.g. root systems in a `World`, or when using `Store` without `World`. + +We decided to continue to always generate a set of functions that dynamically decide which `Store` to use, so that the generated table libraries can still be imported by non-root systems. + +```solidity +library Counter { + // Dynamically determine which store to write to based on the context + function set(uint32 value) internal; + + // Always write to own storage + function _set(uint32 value) internal; + + // ... equivalent functions for all other Store methods +} +``` diff --git a/.gitattributes b/.gitattributes index a21dceecec..ee7e869fad 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,3 @@ # suppress diffs for codegen in PRs **/codegen/**/*.sol linguist-generated=true +**/test-data/**/*.json linguist-generated=true diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 3934bb9ce7..31ba84fd42 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -1,13 +1,7 @@ name: Docker on: - push: - branches: - - main - tags: - # only target a single package tag to avoid running this task for every package on a version release - # in the metadata step, we parse out the specific version used in the tag (e.g. `2.0.0-next.5` and `next`) - - "@latticexyz/common@*" + workflow_call: workflow_dispatch: jobs: @@ -37,6 +31,7 @@ jobs: id: meta uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7 with: + # If this doesn't tag properly within the context of `workflow_call`, try `context: git` images: ${{ matrix.image }} tags: | type=raw,value=latest,enable={{is_default_branch}} diff --git a/.github/workflows/prerelease.yml b/.github/workflows/prerelease.yml index 6032d2fbaa..9ce49b86b0 100644 --- a/.github/workflows/prerelease.yml +++ b/.github/workflows/prerelease.yml @@ -33,7 +33,7 @@ jobs: run: npm config set '//registry.npmjs.org/:_authToken' "${NPM_TOKEN}" env: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - + - name: Check for pre.json file existence id: check_files uses: andstor/file-existence-action@v2.0.0 @@ -47,10 +47,13 @@ jobs: run: npx changeset pre enter next - name: Create next version PR or publish 🚀 - uses: changesets/action@v1 + uses: changesets/action@v1 with: version: pnpm release:version publish: pnpm release:publish env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + docker: + uses: ./.github/workflows/docker.yml + needs: prerelease diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a0d192c7c3..4a81b3b260 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -24,7 +24,7 @@ jobs: - name: "Setup" uses: ./.github/actions/setup - + - name: Clean shell: bash run: pnpm turbo run clean --force --concurrency 10 @@ -60,3 +60,6 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + docker: + uses: ./.github/workflows/docker.yml + needs: version diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml index 4837cd0d09..13d4df3c8c 100644 --- a/.github/workflows/snapshot.yml +++ b/.github/workflows/snapshot.yml @@ -48,3 +48,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + + docker: + uses: ./.github/workflows/docker.yml + needs: release-snapshot diff --git a/Dockerfile b/Dockerfile index 6b4a5ef2c8..31e8acc9eb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -50,3 +50,7 @@ RUN pnpm run -r build FROM mud AS store-indexer WORKDIR /app/packages/store-indexer EXPOSE 3001 + +FROM mud AS faucet +WORKDIR /app/packages/faucet +EXPOSE 3002 diff --git a/e2e/packages/client-vanilla/src/mud/setupNetwork.ts b/e2e/packages/client-vanilla/src/mud/setupNetwork.ts index ae79c26916..34cb6e7329 100644 --- a/e2e/packages/client-vanilla/src/mud/setupNetwork.ts +++ b/e2e/packages/client-vanilla/src/mud/setupNetwork.ts @@ -33,7 +33,7 @@ export async function setupNetwork() { walletClient: burnerWalletClient, }); - const { components, latestBlock$, blockStorageOperations$, waitForTransaction } = await syncToRecs({ + const { components, latestBlock$, storedBlockLogs$, waitForTransaction } = await syncToRecs({ world, config: mudConfig, address: networkConfig.worldAddress as Hex, @@ -74,7 +74,7 @@ export async function setupNetwork() { walletClient: burnerWalletClient, worldContract, latestBlock$, - blockStorageOperations$, + storedBlockLogs$, waitForTransaction, }; } diff --git a/e2e/packages/contracts/src/codegen/tables/Multi.sol b/e2e/packages/contracts/src/codegen/tables/Multi.sol index a634403932..fd30c691ef 100644 --- a/e2e/packages/contracts/src/codegen/tables/Multi.sol +++ b/e2e/packages/contracts/src/codegen/tables/Multi.sol @@ -21,6 +21,10 @@ import { PackedCounter, PackedCounterLib } from "@latticexyz/store/src/PackedCou bytes32 constant _tableId = bytes32(abi.encodePacked(bytes16(""), bytes16("Multi"))); bytes32 constant MultiTableId = _tableId; +FieldLayout constant _fieldLayout = FieldLayout.wrap( + 0x0021020020010000000000000000000000000000000000000000000000000000 +); + struct MultiData { int256 num; bool value; @@ -29,11 +33,7 @@ struct MultiData { library Multi { /** Get the table values' field layout */ function getFieldLayout() internal pure returns (FieldLayout) { - uint256[] memory _fieldLayout = new uint256[](2); - _fieldLayout[0] = 32; - _fieldLayout[1] = 1; - - return FieldLayoutLib.encode(_fieldLayout, 0); + return _fieldLayout; } /** Get the table's key schema */ @@ -74,19 +74,17 @@ library Multi { /** Register the table with its config */ function register() internal { - StoreSwitch.registerTable( - _tableId, - getFieldLayout(), - getKeySchema(), - getValueSchema(), - getKeyNames(), - getFieldNames() - ); + StoreSwitch.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + } + + /** Register the table with its config */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Register the table with its config (using the specified store) */ function register(IStore _store) internal { - _store.registerTable(_tableId, getFieldLayout(), getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + _store.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Get num */ @@ -97,8 +95,20 @@ library Multi { _keyTuple[2] = bytes32(uint256(c)); _keyTuple[3] = bytes32(uint256(int256(d))); - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (int256(uint256(Bytes.slice32(_blob, 0)))); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (int256(uint256(bytes32(_blob)))); + } + + /** Get num */ + function _getNum(uint32 a, bool b, uint256 c, int120 d) internal view returns (int256 num) { + bytes32[] memory _keyTuple = new bytes32[](4); + _keyTuple[0] = bytes32(uint256(a)); + _keyTuple[1] = _boolToBytes32(b); + _keyTuple[2] = bytes32(uint256(c)); + _keyTuple[3] = bytes32(uint256(int256(d))); + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (int256(uint256(bytes32(_blob)))); } /** Get num (using the specified store) */ @@ -109,8 +119,8 @@ library Multi { _keyTuple[2] = bytes32(uint256(c)); _keyTuple[3] = bytes32(uint256(int256(d))); - bytes memory _blob = _store.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (int256(uint256(Bytes.slice32(_blob, 0)))); + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (int256(uint256(bytes32(_blob)))); } /** Set num */ @@ -121,7 +131,18 @@ library Multi { _keyTuple[2] = bytes32(uint256(c)); _keyTuple[3] = bytes32(uint256(int256(d))); - StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((num)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((num)), _fieldLayout); + } + + /** Set num */ + function _setNum(uint32 a, bool b, uint256 c, int120 d, int256 num) internal { + bytes32[] memory _keyTuple = new bytes32[](4); + _keyTuple[0] = bytes32(uint256(a)); + _keyTuple[1] = _boolToBytes32(b); + _keyTuple[2] = bytes32(uint256(c)); + _keyTuple[3] = bytes32(uint256(int256(d))); + + StoreCore.setField(_tableId, _keyTuple, 0, abi.encodePacked((num)), _fieldLayout); } /** Set num (using the specified store) */ @@ -132,7 +153,7 @@ library Multi { _keyTuple[2] = bytes32(uint256(c)); _keyTuple[3] = bytes32(uint256(int256(d))); - _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((num)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((num)), _fieldLayout); } /** Get value */ @@ -143,8 +164,20 @@ library Multi { _keyTuple[2] = bytes32(uint256(c)); _keyTuple[3] = bytes32(uint256(int256(d))); - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 1, getFieldLayout()); - return (_toBool(uint8(Bytes.slice1(_blob, 0)))); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 1, _fieldLayout); + return (_toBool(uint8(bytes1(_blob)))); + } + + /** Get value */ + function _getValue(uint32 a, bool b, uint256 c, int120 d) internal view returns (bool value) { + bytes32[] memory _keyTuple = new bytes32[](4); + _keyTuple[0] = bytes32(uint256(a)); + _keyTuple[1] = _boolToBytes32(b); + _keyTuple[2] = bytes32(uint256(c)); + _keyTuple[3] = bytes32(uint256(int256(d))); + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 1, _fieldLayout); + return (_toBool(uint8(bytes1(_blob)))); } /** Get value (using the specified store) */ @@ -155,8 +188,8 @@ library Multi { _keyTuple[2] = bytes32(uint256(c)); _keyTuple[3] = bytes32(uint256(int256(d))); - bytes memory _blob = _store.getField(_tableId, _keyTuple, 1, getFieldLayout()); - return (_toBool(uint8(Bytes.slice1(_blob, 0)))); + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 1, _fieldLayout); + return (_toBool(uint8(bytes1(_blob)))); } /** Set value */ @@ -167,7 +200,18 @@ library Multi { _keyTuple[2] = bytes32(uint256(c)); _keyTuple[3] = bytes32(uint256(int256(d))); - StoreSwitch.setField(_tableId, _keyTuple, 1, abi.encodePacked((value)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 1, abi.encodePacked((value)), _fieldLayout); + } + + /** Set value */ + function _setValue(uint32 a, bool b, uint256 c, int120 d, bool value) internal { + bytes32[] memory _keyTuple = new bytes32[](4); + _keyTuple[0] = bytes32(uint256(a)); + _keyTuple[1] = _boolToBytes32(b); + _keyTuple[2] = bytes32(uint256(c)); + _keyTuple[3] = bytes32(uint256(int256(d))); + + StoreCore.setField(_tableId, _keyTuple, 1, abi.encodePacked((value)), _fieldLayout); } /** Set value (using the specified store) */ @@ -178,7 +222,7 @@ library Multi { _keyTuple[2] = bytes32(uint256(c)); _keyTuple[3] = bytes32(uint256(int256(d))); - _store.setField(_tableId, _keyTuple, 1, abi.encodePacked((value)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 1, abi.encodePacked((value)), _fieldLayout); } /** Get the full data */ @@ -189,7 +233,19 @@ library Multi { _keyTuple[2] = bytes32(uint256(c)); _keyTuple[3] = bytes32(uint256(int256(d))); - bytes memory _blob = StoreSwitch.getRecord(_tableId, _keyTuple, getFieldLayout()); + bytes memory _blob = StoreSwitch.getRecord(_tableId, _keyTuple, _fieldLayout); + return decode(_blob); + } + + /** Get the full data */ + function _get(uint32 a, bool b, uint256 c, int120 d) internal view returns (MultiData memory _table) { + bytes32[] memory _keyTuple = new bytes32[](4); + _keyTuple[0] = bytes32(uint256(a)); + _keyTuple[1] = _boolToBytes32(b); + _keyTuple[2] = bytes32(uint256(c)); + _keyTuple[3] = bytes32(uint256(int256(d))); + + bytes memory _blob = StoreCore.getRecord(_tableId, _keyTuple, _fieldLayout); return decode(_blob); } @@ -201,13 +257,32 @@ library Multi { _keyTuple[2] = bytes32(uint256(c)); _keyTuple[3] = bytes32(uint256(int256(d))); - bytes memory _blob = _store.getRecord(_tableId, _keyTuple, getFieldLayout()); + bytes memory _blob = _store.getRecord(_tableId, _keyTuple, _fieldLayout); return decode(_blob); } /** Set the full data using individual values */ function set(uint32 a, bool b, uint256 c, int120 d, int256 num, bool value) internal { - bytes memory _data = encode(num, value); + bytes memory _staticData = encodeStatic(num, value); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; + + bytes32[] memory _keyTuple = new bytes32[](4); + _keyTuple[0] = bytes32(uint256(a)); + _keyTuple[1] = _boolToBytes32(b); + _keyTuple[2] = bytes32(uint256(c)); + _keyTuple[3] = bytes32(uint256(int256(d))); + + StoreSwitch.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); + } + + /** Set the full data using individual values */ + function _set(uint32 a, bool b, uint256 c, int120 d, int256 num, bool value) internal { + bytes memory _staticData = encodeStatic(num, value); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; bytes32[] memory _keyTuple = new bytes32[](4); _keyTuple[0] = bytes32(uint256(a)); @@ -215,12 +290,15 @@ library Multi { _keyTuple[2] = bytes32(uint256(c)); _keyTuple[3] = bytes32(uint256(int256(d))); - StoreSwitch.setRecord(_tableId, _keyTuple, _data, getFieldLayout()); + StoreCore.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); } /** Set the full data using individual values (using the specified store) */ function set(IStore _store, uint32 a, bool b, uint256 c, int120 d, int256 num, bool value) internal { - bytes memory _data = encode(num, value); + bytes memory _staticData = encodeStatic(num, value); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; bytes32[] memory _keyTuple = new bytes32[](4); _keyTuple[0] = bytes32(uint256(a)); @@ -228,7 +306,7 @@ library Multi { _keyTuple[2] = bytes32(uint256(c)); _keyTuple[3] = bytes32(uint256(int256(d))); - _store.setRecord(_tableId, _keyTuple, _data, getFieldLayout()); + _store.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); } /** Set the full data using the data struct */ @@ -236,6 +314,11 @@ library Multi { set(a, b, c, d, _table.num, _table.value); } + /** Set the full data using the data struct */ + function _set(uint32 a, bool b, uint256 c, int120 d, MultiData memory _table) internal { + set(a, b, c, d, _table.num, _table.value); + } + /** Set the full data using the data struct (using the specified store) */ function set(IStore _store, uint32 a, bool b, uint256 c, int120 d, MultiData memory _table) internal { set(_store, a, b, c, d, _table.num, _table.value); @@ -248,9 +331,19 @@ library Multi { _table.value = (_toBool(uint8(Bytes.slice1(_blob, 32)))); } + /** Tightly pack static data using this table's schema */ + function encodeStatic(int256 num, bool value) internal pure returns (bytes memory) { + return abi.encodePacked(num, value); + } + /** Tightly pack full data using this table's field layout */ function encode(int256 num, bool value) internal pure returns (bytes memory) { - return abi.encodePacked(num, value); + bytes memory _staticData = encodeStatic(num, value); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; + + return abi.encodePacked(_staticData, _encodedLengths, _dynamicData); } /** Encode keys as a bytes32 array using this table's field layout */ @@ -272,7 +365,18 @@ library Multi { _keyTuple[2] = bytes32(uint256(c)); _keyTuple[3] = bytes32(uint256(int256(d))); - StoreSwitch.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + StoreSwitch.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /* Delete all data for given keys */ + function _deleteRecord(uint32 a, bool b, uint256 c, int120 d) internal { + bytes32[] memory _keyTuple = new bytes32[](4); + _keyTuple[0] = bytes32(uint256(a)); + _keyTuple[1] = _boolToBytes32(b); + _keyTuple[2] = bytes32(uint256(c)); + _keyTuple[3] = bytes32(uint256(int256(d))); + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); } /* Delete all data for given keys (using the specified store) */ @@ -283,7 +387,7 @@ library Multi { _keyTuple[2] = bytes32(uint256(c)); _keyTuple[3] = bytes32(uint256(int256(d))); - _store.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + _store.deleteRecord(_tableId, _keyTuple, _fieldLayout); } } diff --git a/e2e/packages/contracts/src/codegen/tables/Number.sol b/e2e/packages/contracts/src/codegen/tables/Number.sol index 01127f809c..77dcf598c3 100644 --- a/e2e/packages/contracts/src/codegen/tables/Number.sol +++ b/e2e/packages/contracts/src/codegen/tables/Number.sol @@ -21,13 +21,14 @@ import { PackedCounter, PackedCounterLib } from "@latticexyz/store/src/PackedCou bytes32 constant _tableId = bytes32(abi.encodePacked(bytes16(""), bytes16("Number"))); bytes32 constant NumberTableId = _tableId; +FieldLayout constant _fieldLayout = FieldLayout.wrap( + 0x0004010004000000000000000000000000000000000000000000000000000000 +); + library Number { /** Get the table values' field layout */ function getFieldLayout() internal pure returns (FieldLayout) { - uint256[] memory _fieldLayout = new uint256[](1); - _fieldLayout[0] = 4; - - return FieldLayoutLib.encode(_fieldLayout, 0); + return _fieldLayout; } /** Get the table's key schema */ @@ -60,19 +61,17 @@ library Number { /** Register the table with its config */ function register() internal { - StoreSwitch.registerTable( - _tableId, - getFieldLayout(), - getKeySchema(), - getValueSchema(), - getKeyNames(), - getFieldNames() - ); + StoreSwitch.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + } + + /** Register the table with its config */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Register the table with its config (using the specified store) */ function register(IStore _store) internal { - _store.registerTable(_tableId, getFieldLayout(), getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + _store.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Get value */ @@ -80,8 +79,17 @@ library Number { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(uint256(key)); - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (uint32(Bytes.slice4(_blob, 0))); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint32(bytes4(_blob))); + } + + /** Get value */ + function _get(uint32 key) internal view returns (uint32 value) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(key)); + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint32(bytes4(_blob))); } /** Get value (using the specified store) */ @@ -89,8 +97,8 @@ library Number { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(uint256(key)); - bytes memory _blob = _store.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (uint32(Bytes.slice4(_blob, 0))); + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint32(bytes4(_blob))); } /** Set value */ @@ -98,7 +106,15 @@ library Number { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(uint256(key)); - StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((value)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); + } + + /** Set value */ + function _set(uint32 key, uint32 value) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(key)); + + StoreCore.setField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); } /** Set value (using the specified store) */ @@ -106,12 +122,22 @@ library Number { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(uint256(key)); - _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((value)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); + } + + /** Tightly pack static data using this table's schema */ + function encodeStatic(uint32 value) internal pure returns (bytes memory) { + return abi.encodePacked(value); } /** Tightly pack full data using this table's field layout */ function encode(uint32 value) internal pure returns (bytes memory) { - return abi.encodePacked(value); + bytes memory _staticData = encodeStatic(value); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; + + return abi.encodePacked(_staticData, _encodedLengths, _dynamicData); } /** Encode keys as a bytes32 array using this table's field layout */ @@ -127,7 +153,15 @@ library Number { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(uint256(key)); - StoreSwitch.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + StoreSwitch.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /* Delete all data for given keys */ + function _deleteRecord(uint32 key) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(key)); + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); } /* Delete all data for given keys (using the specified store) */ @@ -135,6 +169,6 @@ library Number { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(uint256(key)); - _store.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + _store.deleteRecord(_tableId, _keyTuple, _fieldLayout); } } diff --git a/e2e/packages/contracts/src/codegen/tables/NumberList.sol b/e2e/packages/contracts/src/codegen/tables/NumberList.sol index 60d34cc507..2703550074 100644 --- a/e2e/packages/contracts/src/codegen/tables/NumberList.sol +++ b/e2e/packages/contracts/src/codegen/tables/NumberList.sol @@ -21,12 +21,14 @@ import { PackedCounter, PackedCounterLib } from "@latticexyz/store/src/PackedCou bytes32 constant _tableId = bytes32(abi.encodePacked(bytes16(""), bytes16("NumberList"))); bytes32 constant NumberListTableId = _tableId; +FieldLayout constant _fieldLayout = FieldLayout.wrap( + 0x0000000100000000000000000000000000000000000000000000000000000000 +); + library NumberList { /** Get the table values' field layout */ function getFieldLayout() internal pure returns (FieldLayout) { - uint256[] memory _fieldLayout = new uint256[](0); - - return FieldLayoutLib.encode(_fieldLayout, 1); + return _fieldLayout; } /** Get the table's key schema */ @@ -57,26 +59,32 @@ library NumberList { /** Register the table with its config */ function register() internal { - StoreSwitch.registerTable( - _tableId, - getFieldLayout(), - getKeySchema(), - getValueSchema(), - getKeyNames(), - getFieldNames() - ); + StoreSwitch.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + } + + /** Register the table with its config */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Register the table with its config (using the specified store) */ function register(IStore _store) internal { - _store.registerTable(_tableId, getFieldLayout(), getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + _store.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Get value */ function get() internal view returns (uint32[] memory value) { bytes32[] memory _keyTuple = new bytes32[](0); - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 0, getFieldLayout()); + bytes memory _blob = StoreSwitch.getDynamicField(_tableId, _keyTuple, 0); + return (SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_uint32()); + } + + /** Get value */ + function _get() internal view returns (uint32[] memory value) { + bytes32[] memory _keyTuple = new bytes32[](0); + + bytes memory _blob = StoreCore.getDynamicField(_tableId, _keyTuple, 0); return (SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_uint32()); } @@ -84,7 +92,7 @@ library NumberList { function get(IStore _store) internal view returns (uint32[] memory value) { bytes32[] memory _keyTuple = new bytes32[](0); - bytes memory _blob = _store.getField(_tableId, _keyTuple, 0, getFieldLayout()); + bytes memory _blob = _store.getDynamicField(_tableId, _keyTuple, 0); return (SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_uint32()); } @@ -92,21 +100,38 @@ library NumberList { function set(uint32[] memory value) internal { bytes32[] memory _keyTuple = new bytes32[](0); - StoreSwitch.setField(_tableId, _keyTuple, 0, EncodeArray.encode((value)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 0, EncodeArray.encode((value)), _fieldLayout); + } + + /** Set value */ + function _set(uint32[] memory value) internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreCore.setField(_tableId, _keyTuple, 0, EncodeArray.encode((value)), _fieldLayout); } /** Set value (using the specified store) */ function set(IStore _store, uint32[] memory value) internal { bytes32[] memory _keyTuple = new bytes32[](0); - _store.setField(_tableId, _keyTuple, 0, EncodeArray.encode((value)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 0, EncodeArray.encode((value)), _fieldLayout); } /** Get the length of value */ function length() internal view returns (uint256) { bytes32[] memory _keyTuple = new bytes32[](0); - uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 0, getFieldLayout()); + uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 0, _fieldLayout); + unchecked { + return _byteLength / 4; + } + } + + /** Get the length of value */ + function _length() internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](0); + + uint256 _byteLength = StoreCore.getFieldLength(_tableId, _keyTuple, 0, _fieldLayout); unchecked { return _byteLength / 4; } @@ -116,7 +141,7 @@ library NumberList { function length(IStore _store) internal view returns (uint256) { bytes32[] memory _keyTuple = new bytes32[](0); - uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 0, getFieldLayout()); + uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 0, _fieldLayout); unchecked { return _byteLength / 4; } @@ -134,11 +159,24 @@ library NumberList { _tableId, _keyTuple, 0, - getFieldLayout(), + _fieldLayout, _index * 4, (_index + 1) * 4 ); - return (uint32(Bytes.slice4(_blob, 0))); + return (uint32(bytes4(_blob))); + } + } + + /** + * Get an item of value + * (unchecked, returns invalid data if index overflows) + */ + function _getItem(uint256 _index) internal view returns (uint32) { + bytes32[] memory _keyTuple = new bytes32[](0); + + unchecked { + bytes memory _blob = StoreCore.getFieldSlice(_tableId, _keyTuple, 0, _fieldLayout, _index * 4, (_index + 1) * 4); + return (uint32(bytes4(_blob))); } } @@ -150,8 +188,8 @@ library NumberList { bytes32[] memory _keyTuple = new bytes32[](0); unchecked { - bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 0, getFieldLayout(), _index * 4, (_index + 1) * 4); - return (uint32(Bytes.slice4(_blob, 0))); + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 0, _fieldLayout, _index * 4, (_index + 1) * 4); + return (uint32(bytes4(_blob))); } } @@ -159,28 +197,42 @@ library NumberList { function push(uint32 _element) internal { bytes32[] memory _keyTuple = new bytes32[](0); - StoreSwitch.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), getFieldLayout()); + StoreSwitch.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), _fieldLayout); + } + + /** Push an element to value */ + function _push(uint32 _element) internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreCore.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), _fieldLayout); } /** Push an element to value (using the specified store) */ function push(IStore _store, uint32 _element) internal { bytes32[] memory _keyTuple = new bytes32[](0); - _store.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), getFieldLayout()); + _store.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), _fieldLayout); } /** Pop an element from value */ function pop() internal { bytes32[] memory _keyTuple = new bytes32[](0); - StoreSwitch.popFromField(_tableId, _keyTuple, 0, 4, getFieldLayout()); + StoreSwitch.popFromField(_tableId, _keyTuple, 0, 4, _fieldLayout); + } + + /** Pop an element from value */ + function _pop() internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreCore.popFromField(_tableId, _keyTuple, 0, 4, _fieldLayout); } /** Pop an element from value (using the specified store) */ function pop(IStore _store) internal { bytes32[] memory _keyTuple = new bytes32[](0); - _store.popFromField(_tableId, _keyTuple, 0, 4, getFieldLayout()); + _store.popFromField(_tableId, _keyTuple, 0, 4, _fieldLayout); } /** @@ -191,7 +243,19 @@ library NumberList { bytes32[] memory _keyTuple = new bytes32[](0); unchecked { - StoreSwitch.updateInField(_tableId, _keyTuple, 0, _index * 4, abi.encodePacked((_element)), getFieldLayout()); + StoreSwitch.updateInField(_tableId, _keyTuple, 0, _index * 4, abi.encodePacked((_element)), _fieldLayout); + } + } + + /** + * Update an element of value at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ + function _update(uint256 _index, uint32 _element) internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + unchecked { + StoreCore.updateInField(_tableId, _keyTuple, 0, _index * 4, abi.encodePacked((_element)), _fieldLayout); } } @@ -203,19 +267,30 @@ library NumberList { bytes32[] memory _keyTuple = new bytes32[](0); unchecked { - _store.updateInField(_tableId, _keyTuple, 0, _index * 4, abi.encodePacked((_element)), getFieldLayout()); + _store.updateInField(_tableId, _keyTuple, 0, _index * 4, abi.encodePacked((_element)), _fieldLayout); } } - /** Tightly pack full data using this table's field layout */ - function encode(uint32[] memory value) internal pure returns (bytes memory) { - PackedCounter _encodedLengths; + /** Tightly pack dynamic data using this table's schema */ + function encodeLengths(uint32[] memory value) internal pure returns (PackedCounter _encodedLengths) { // Lengths are effectively checked during copy by 2**40 bytes exceeding gas limits unchecked { _encodedLengths = PackedCounterLib.pack(value.length * 4); } + } + + /** Tightly pack dynamic data using this table's schema */ + function encodeDynamic(uint32[] memory value) internal pure returns (bytes memory) { + return abi.encodePacked(EncodeArray.encode((value))); + } - return abi.encodePacked(_encodedLengths.unwrap(), EncodeArray.encode((value))); + /** Tightly pack full data using this table's field layout */ + function encode(uint32[] memory value) internal pure returns (bytes memory) { + bytes memory _staticData; + PackedCounter _encodedLengths = encodeLengths(value); + bytes memory _dynamicData = encodeDynamic(value); + + return abi.encodePacked(_staticData, _encodedLengths, _dynamicData); } /** Encode keys as a bytes32 array using this table's field layout */ @@ -229,13 +304,20 @@ library NumberList { function deleteRecord() internal { bytes32[] memory _keyTuple = new bytes32[](0); - StoreSwitch.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + StoreSwitch.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /* Delete all data for given keys */ + function _deleteRecord() internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); } /* Delete all data for given keys (using the specified store) */ function deleteRecord(IStore _store) internal { bytes32[] memory _keyTuple = new bytes32[](0); - _store.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + _store.deleteRecord(_tableId, _keyTuple, _fieldLayout); } } diff --git a/e2e/packages/contracts/src/codegen/tables/Vector.sol b/e2e/packages/contracts/src/codegen/tables/Vector.sol index e258fac50c..380f222702 100644 --- a/e2e/packages/contracts/src/codegen/tables/Vector.sol +++ b/e2e/packages/contracts/src/codegen/tables/Vector.sol @@ -21,6 +21,10 @@ import { PackedCounter, PackedCounterLib } from "@latticexyz/store/src/PackedCou bytes32 constant _tableId = bytes32(abi.encodePacked(bytes16(""), bytes16("Vector"))); bytes32 constant VectorTableId = _tableId; +FieldLayout constant _fieldLayout = FieldLayout.wrap( + 0x0008020004040000000000000000000000000000000000000000000000000000 +); + struct VectorData { int32 x; int32 y; @@ -29,11 +33,7 @@ struct VectorData { library Vector { /** Get the table values' field layout */ function getFieldLayout() internal pure returns (FieldLayout) { - uint256[] memory _fieldLayout = new uint256[](2); - _fieldLayout[0] = 4; - _fieldLayout[1] = 4; - - return FieldLayoutLib.encode(_fieldLayout, 0); + return _fieldLayout; } /** Get the table's key schema */ @@ -68,19 +68,17 @@ library Vector { /** Register the table with its config */ function register() internal { - StoreSwitch.registerTable( - _tableId, - getFieldLayout(), - getKeySchema(), - getValueSchema(), - getKeyNames(), - getFieldNames() - ); + StoreSwitch.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + } + + /** Register the table with its config */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Register the table with its config (using the specified store) */ function register(IStore _store) internal { - _store.registerTable(_tableId, getFieldLayout(), getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + _store.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Get x */ @@ -88,8 +86,17 @@ library Vector { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(uint256(key)); - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (int32(uint32(Bytes.slice4(_blob, 0)))); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (int32(uint32(bytes4(_blob)))); + } + + /** Get x */ + function _getX(uint32 key) internal view returns (int32 x) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(key)); + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (int32(uint32(bytes4(_blob)))); } /** Get x (using the specified store) */ @@ -97,8 +104,8 @@ library Vector { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(uint256(key)); - bytes memory _blob = _store.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (int32(uint32(Bytes.slice4(_blob, 0)))); + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (int32(uint32(bytes4(_blob)))); } /** Set x */ @@ -106,7 +113,15 @@ library Vector { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(uint256(key)); - StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((x)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((x)), _fieldLayout); + } + + /** Set x */ + function _setX(uint32 key, int32 x) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(key)); + + StoreCore.setField(_tableId, _keyTuple, 0, abi.encodePacked((x)), _fieldLayout); } /** Set x (using the specified store) */ @@ -114,7 +129,7 @@ library Vector { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(uint256(key)); - _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((x)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((x)), _fieldLayout); } /** Get y */ @@ -122,8 +137,17 @@ library Vector { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(uint256(key)); - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 1, getFieldLayout()); - return (int32(uint32(Bytes.slice4(_blob, 0)))); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 1, _fieldLayout); + return (int32(uint32(bytes4(_blob)))); + } + + /** Get y */ + function _getY(uint32 key) internal view returns (int32 y) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(key)); + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 1, _fieldLayout); + return (int32(uint32(bytes4(_blob)))); } /** Get y (using the specified store) */ @@ -131,8 +155,8 @@ library Vector { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(uint256(key)); - bytes memory _blob = _store.getField(_tableId, _keyTuple, 1, getFieldLayout()); - return (int32(uint32(Bytes.slice4(_blob, 0)))); + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 1, _fieldLayout); + return (int32(uint32(bytes4(_blob)))); } /** Set y */ @@ -140,7 +164,15 @@ library Vector { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(uint256(key)); - StoreSwitch.setField(_tableId, _keyTuple, 1, abi.encodePacked((y)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 1, abi.encodePacked((y)), _fieldLayout); + } + + /** Set y */ + function _setY(uint32 key, int32 y) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(key)); + + StoreCore.setField(_tableId, _keyTuple, 1, abi.encodePacked((y)), _fieldLayout); } /** Set y (using the specified store) */ @@ -148,7 +180,7 @@ library Vector { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(uint256(key)); - _store.setField(_tableId, _keyTuple, 1, abi.encodePacked((y)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 1, abi.encodePacked((y)), _fieldLayout); } /** Get the full data */ @@ -156,7 +188,16 @@ library Vector { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(uint256(key)); - bytes memory _blob = StoreSwitch.getRecord(_tableId, _keyTuple, getFieldLayout()); + bytes memory _blob = StoreSwitch.getRecord(_tableId, _keyTuple, _fieldLayout); + return decode(_blob); + } + + /** Get the full data */ + function _get(uint32 key) internal view returns (VectorData memory _table) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(key)); + + bytes memory _blob = StoreCore.getRecord(_tableId, _keyTuple, _fieldLayout); return decode(_blob); } @@ -165,28 +206,47 @@ library Vector { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(uint256(key)); - bytes memory _blob = _store.getRecord(_tableId, _keyTuple, getFieldLayout()); + bytes memory _blob = _store.getRecord(_tableId, _keyTuple, _fieldLayout); return decode(_blob); } /** Set the full data using individual values */ function set(uint32 key, int32 x, int32 y) internal { - bytes memory _data = encode(x, y); + bytes memory _staticData = encodeStatic(x, y); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; + + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(key)); + + StoreSwitch.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); + } + + /** Set the full data using individual values */ + function _set(uint32 key, int32 x, int32 y) internal { + bytes memory _staticData = encodeStatic(x, y); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(uint256(key)); - StoreSwitch.setRecord(_tableId, _keyTuple, _data, getFieldLayout()); + StoreCore.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); } /** Set the full data using individual values (using the specified store) */ function set(IStore _store, uint32 key, int32 x, int32 y) internal { - bytes memory _data = encode(x, y); + bytes memory _staticData = encodeStatic(x, y); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(uint256(key)); - _store.setRecord(_tableId, _keyTuple, _data, getFieldLayout()); + _store.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); } /** Set the full data using the data struct */ @@ -194,6 +254,11 @@ library Vector { set(key, _table.x, _table.y); } + /** Set the full data using the data struct */ + function _set(uint32 key, VectorData memory _table) internal { + set(key, _table.x, _table.y); + } + /** Set the full data using the data struct (using the specified store) */ function set(IStore _store, uint32 key, VectorData memory _table) internal { set(_store, key, _table.x, _table.y); @@ -206,9 +271,19 @@ library Vector { _table.y = (int32(uint32(Bytes.slice4(_blob, 4)))); } + /** Tightly pack static data using this table's schema */ + function encodeStatic(int32 x, int32 y) internal pure returns (bytes memory) { + return abi.encodePacked(x, y); + } + /** Tightly pack full data using this table's field layout */ function encode(int32 x, int32 y) internal pure returns (bytes memory) { - return abi.encodePacked(x, y); + bytes memory _staticData = encodeStatic(x, y); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; + + return abi.encodePacked(_staticData, _encodedLengths, _dynamicData); } /** Encode keys as a bytes32 array using this table's field layout */ @@ -224,7 +299,15 @@ library Vector { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(uint256(key)); - StoreSwitch.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + StoreSwitch.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /* Delete all data for given keys */ + function _deleteRecord(uint32 key) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(key)); + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); } /* Delete all data for given keys (using the specified store) */ @@ -232,6 +315,6 @@ library Vector { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(uint256(key)); - _store.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + _store.deleteRecord(_tableId, _keyTuple, _fieldLayout); } } diff --git a/e2e/packages/contracts/worlds.json b/e2e/packages/contracts/worlds.json index bd48105fd7..a9623cfa62 100644 --- a/e2e/packages/contracts/worlds.json +++ b/e2e/packages/contracts/worlds.json @@ -1,5 +1,5 @@ { "31337": { - "address": "0x5FbDB2315678afecb367f032d93F642f64180aa3" + "address": "0x3Aa5ebB10DC797CAC828524e59A333d0A371443c" } } \ No newline at end of file diff --git a/e2e/packages/sync-test/data/encodeTestData.test.ts b/e2e/packages/sync-test/data/encodeTestData.test.ts index df67bb56e2..11c919ac0e 100644 --- a/e2e/packages/sync-test/data/encodeTestData.test.ts +++ b/e2e/packages/sync-test/data/encodeTestData.test.ts @@ -3,24 +3,36 @@ import { encodeTestData } from "./encodeTestData"; describe("encodeTestData", () => { it("should encode numbers", () => { - expect(encodeTestData({ Number: [{ key: { key: 42 }, value: { value: 1337 } }] })).toStrictEqual({ - Number: [ - { - key: ["0x000000000000000000000000000000000000000000000000000000000000002a"], - value: "0x00000539", - valueSchema: "0x0004010003000000000000000000000000000000000000000000000000000000", - }, - ], - }); + expect(encodeTestData({ Number: [{ key: { key: 42 }, value: { value: 1337 } }] })).toMatchInlineSnapshot(` + { + "Number": [ + { + "dynamicData": "0x", + "encodedLengths": "0x0000000000000000000000000000000000000000000000000000000000000000", + "fieldLayout": "0x0004010004000000000000000000000000000000000000000000000000000000", + "key": [ + "0x000000000000000000000000000000000000000000000000000000000000002a", + ], + "staticData": "0x00000539", + }, + ], + } + `); - expect(encodeTestData({ Vector: [{ key: { key: 1337 }, value: { x: 42, y: -69 } }] })).toStrictEqual({ - Vector: [ - { - key: ["0x0000000000000000000000000000000000000000000000000000000000000539"], - value: "0x0000002affffffbb", - valueSchema: "0x0008020023230000000000000000000000000000000000000000000000000000", - }, - ], - }); + expect(encodeTestData({ Vector: [{ key: { key: 1337 }, value: { x: 42, y: -69 } }] })).toMatchInlineSnapshot(` + { + "Vector": [ + { + "dynamicData": "0x", + "encodedLengths": "0x0000000000000000000000000000000000000000000000000000000000000000", + "fieldLayout": "0x0008020004040000000000000000000000000000000000000000000000000000", + "key": [ + "0x0000000000000000000000000000000000000000000000000000000000000539", + ], + "staticData": "0x0000002affffffbb", + }, + ], + } + `); }); }); diff --git a/e2e/packages/sync-test/data/encodeTestData.ts b/e2e/packages/sync-test/data/encodeTestData.ts index a433626d61..891d770dc4 100644 --- a/e2e/packages/sync-test/data/encodeTestData.ts +++ b/e2e/packages/sync-test/data/encodeTestData.ts @@ -1,9 +1,7 @@ import { mapObject } from "@latticexyz/utils"; +import { encodeKey, encodeValueArgs, valueSchemaToFieldLayoutHex } from "@latticexyz/protocol-parser"; import { Data, EncodedData } from "./types"; -import { encodeAbiParameters, encodePacked } from "viem"; import config from "../../contracts/mud.config"; -import { abiTypesToSchema, schemaToHex } from "@latticexyz/protocol-parser"; -import { StaticAbiType } from "@latticexyz/schema-type"; /** * Turns the typed data into encoded data in the format expected by `world.setRecord` @@ -11,21 +9,16 @@ import { StaticAbiType } from "@latticexyz/schema-type"; export function encodeTestData(testData: Data) { return mapObject(testData, (records, table) => { if (!records) return undefined; - const keyConfig = config.tables[table].keySchema; + const tableConfig = config.tables[table]; return records.map((record) => { - const encodedKey = Object.entries(record.key).map(([keyName, keyValue]) => { - const keyType = keyConfig[keyName as keyof typeof keyConfig] as StaticAbiType; - return encodeAbiParameters([{ type: keyType }], [keyValue]); - }); - - const encodedValue = encodePacked(Object.values(config.tables[table].valueSchema), Object.values(record.value)); - - const encodedValueSchema = schemaToHex(abiTypesToSchema(Object.values(config.tables[table].valueSchema))); + const key = encodeKey(tableConfig.keySchema, record.key); + const valueArgs = encodeValueArgs(tableConfig.valueSchema, record.value); + const fieldLayout = valueSchemaToFieldLayoutHex(tableConfig.valueSchema); return { - key: encodedKey, - value: encodedValue, - valueSchema: encodedValueSchema, + key, + ...valueArgs, + fieldLayout, }; }); }) as EncodedData; diff --git a/e2e/packages/sync-test/data/expectClientData.ts b/e2e/packages/sync-test/data/expectClientData.ts index 6e52c0fc3d..92e81c323f 100644 --- a/e2e/packages/sync-test/data/expectClientData.ts +++ b/e2e/packages/sync-test/data/expectClientData.ts @@ -11,7 +11,7 @@ export async function expectClientData(page: Page, data: Data) { for (const [table, records] of Object.entries(data)) { for (const record of records) { const value = await readComponentValue(page, table, encodeEntity(config.tables[table].keySchema, record.key)); - expect(value).toEqual(record.value); + expect(value).toMatchObject(record.value); } } } diff --git a/e2e/packages/sync-test/data/setContractData.ts b/e2e/packages/sync-test/data/setContractData.ts index 5ca090cae8..323189d95c 100644 --- a/e2e/packages/sync-test/data/setContractData.ts +++ b/e2e/packages/sync-test/data/setContractData.ts @@ -16,8 +16,10 @@ export async function setContractData(page: Page, data: Data) { // TODO: add support for multiple namespaces after https://github.com/latticexyz/mud/issues/994 is resolved tableIdToHex("", table), record.key, - record.value, - record.valueSchema, + record.staticData, + record.encodedLengths, + record.dynamicData, + record.fieldLayout, ]); // Wait for transactions to be confirmed diff --git a/e2e/packages/sync-test/data/types.ts b/e2e/packages/sync-test/data/types.ts index 85abd2c90d..b79ac3f3b6 100644 --- a/e2e/packages/sync-test/data/types.ts +++ b/e2e/packages/sync-test/data/types.ts @@ -20,5 +20,5 @@ export type Datum> }; export type EncodedData = { - [Table in keyof T]: Array<{ key: Hex[]; value: Hex; valueSchema: Hex }>; + [Table in keyof T]: Array<{ key: Hex[]; staticData: Hex; encodedLengths: Hex; dynamicData: Hex; fieldLayout: Hex }>; }; diff --git a/e2e/packages/sync-test/rpcSync.test.ts b/e2e/packages/sync-test/rpcSync.test.ts index 7726f86995..39e786a319 100644 --- a/e2e/packages/sync-test/rpcSync.test.ts +++ b/e2e/packages/sync-test/rpcSync.test.ts @@ -1,7 +1,6 @@ import { afterEach, beforeEach, describe, test } from "vitest"; import type { ViteDevServer } from "vite"; import { Browser, Page } from "@playwright/test"; -import { ExecaChildProcess } from "execa"; import { createAsyncErrorHandler } from "./asyncErrors"; import { deployContracts, startViteServer, startBrowserAndPage, openClientWithRootAccount } from "./setup"; import { diff --git a/e2e/packages/sync-test/setup/startBrowserAndPage.ts b/e2e/packages/sync-test/setup/startBrowserAndPage.ts index f5ab642f9b..0f18151879 100644 --- a/e2e/packages/sync-test/setup/startBrowserAndPage.ts +++ b/e2e/packages/sync-test/setup/startBrowserAndPage.ts @@ -14,7 +14,7 @@ export async function startBrowserAndPage( // log uncaught errors in the browser page (browser and test consoles are separate) page.on("pageerror", (err) => { - console.log(chalk.yellow("[browser page error]:"), err.message); + console.log(chalk.yellow("[browser page error]:"), err.message, err); }); // log browser's console logs diff --git a/e2e/packages/sync-test/vite.config.ts b/e2e/packages/sync-test/vite.config.ts index aee36eb400..c6202f1992 100644 --- a/e2e/packages/sync-test/vite.config.ts +++ b/e2e/packages/sync-test/vite.config.ts @@ -1,6 +1,5 @@ import { defineConfig } from "vitest/config"; -// TODO should this along with `.test.ts` be in `client-vanilla`? export default defineConfig({ test: { environment: "jsdom", diff --git a/examples/minimal/packages/client-phaser/src/mud/setupNetwork.ts b/examples/minimal/packages/client-phaser/src/mud/setupNetwork.ts index 85bac97cd8..144ecd4a33 100644 --- a/examples/minimal/packages/client-phaser/src/mud/setupNetwork.ts +++ b/examples/minimal/packages/client-phaser/src/mud/setupNetwork.ts @@ -36,7 +36,7 @@ export async function setupNetwork() { onWrite: (write) => write$.next(write), }); - const { components, latestBlock$, blockStorageOperations$, waitForTransaction } = await syncToRecs({ + const { components, latestBlock$, storedBlockLogs$, waitForTransaction } = await syncToRecs({ world, config: mudConfig, address: networkConfig.worldAddress as Hex, @@ -75,7 +75,7 @@ export async function setupNetwork() { publicClient, walletClient: burnerWalletClient, latestBlock$, - blockStorageOperations$, + storedBlockLogs$, waitForTransaction, worldContract, write$: write$.asObservable().pipe(share()), diff --git a/examples/minimal/packages/client-phaser/src/ui/App.tsx b/examples/minimal/packages/client-phaser/src/ui/App.tsx index 314e5572d4..69529815b5 100644 --- a/examples/minimal/packages/client-phaser/src/ui/App.tsx +++ b/examples/minimal/packages/client-phaser/src/ui/App.tsx @@ -21,7 +21,7 @@ export const App = () => { publicClient: networkLayer.network.publicClient, walletClient: networkLayer.network.walletClient, latestBlock$: networkLayer.network.latestBlock$, - blockStorageOperations$: networkLayer.network.blockStorageOperations$, + storedBlockLogs$: networkLayer.network.storedBlockLogs$, worldAddress: networkLayer.network.worldContract.address, worldAbi: networkLayer.network.worldContract.abi, write$: networkLayer.network.write$, diff --git a/examples/minimal/packages/client-react/package.json b/examples/minimal/packages/client-react/package.json index cbbce8e319..0b69a60485 100644 --- a/examples/minimal/packages/client-react/package.json +++ b/examples/minimal/packages/client-react/package.json @@ -13,6 +13,7 @@ "@improbable-eng/grpc-web": "^0.15.0", "@latticexyz/common": "link:../../../../packages/common", "@latticexyz/dev-tools": "link:../../../../packages/dev-tools", + "@latticexyz/faucet": "link:../../../../packages/faucet", "@latticexyz/react": "link:../../../../packages/react", "@latticexyz/recs": "link:../../../../packages/recs", "@latticexyz/schema-type": "link:../../../../packages/schema-type", diff --git a/examples/minimal/packages/client-react/src/index.tsx b/examples/minimal/packages/client-react/src/index.tsx index 9b3f9121e1..da8d70f020 100644 --- a/examples/minimal/packages/client-react/src/index.tsx +++ b/examples/minimal/packages/client-react/src/index.tsx @@ -24,7 +24,7 @@ setup().then(async (result) => { publicClient: result.network.publicClient, walletClient: result.network.walletClient, latestBlock$: result.network.latestBlock$, - blockStorageOperations$: result.network.blockStorageOperations$, + storedBlockLogs$: result.network.storedBlockLogs$, worldAddress: result.network.worldContract.address, worldAbi: result.network.worldContract.abi, write$: result.network.write$, diff --git a/examples/minimal/packages/client-react/src/mud/setupNetwork.ts b/examples/minimal/packages/client-react/src/mud/setupNetwork.ts index 9c42a54e14..895bf70553 100644 --- a/examples/minimal/packages/client-react/src/mud/setupNetwork.ts +++ b/examples/minimal/packages/client-react/src/mud/setupNetwork.ts @@ -7,6 +7,7 @@ import IWorldAbi from "contracts/out/IWorld.sol/IWorld.abi.json"; import { ContractWrite, createBurnerAccount, createContract, transportObserver } from "@latticexyz/common"; import { Subject, share } from "rxjs"; import mudConfig from "contracts/mud.config"; +import { createClient as createFaucetClient } from "@latticexyz/faucet"; export type SetupNetworkResult = Awaited>; @@ -36,7 +37,7 @@ export async function setupNetwork() { onWrite: (write) => write$.next(write), }); - const { components, latestBlock$, blockStorageOperations$, waitForTransaction } = await syncToRecs({ + const { components, latestBlock$, storedBlockLogs$, waitForTransaction } = await syncToRecs({ world, config: mudConfig, address: networkConfig.worldAddress as Hex, @@ -44,28 +45,20 @@ export async function setupNetwork() { startBlock: BigInt(networkConfig.initialBlockNumber), }); - // Request drip from faucet - if (networkConfig.faucetServiceUrl) { - const address = burnerAccount.address; - console.info("[Dev Faucet]: Player address -> ", address); + try { + console.log("creating faucet client"); + const faucet = createFaucetClient({ url: "http://localhost:3002/trpc" }); - const faucet = createFaucetService(networkConfig.faucetServiceUrl); - - const requestDrip = async () => { - const balance = await publicClient.getBalance({ address }); - console.info(`[Dev Faucet]: Player balance -> ${balance}`); - const lowBalance = balance < parseEther("1"); - if (lowBalance) { - console.info("[Dev Faucet]: Balance is low, dripping funds to player"); - // Double drip - await faucet.dripDev({ address }); - await faucet.dripDev({ address }); - } + const drip = async () => { + console.log("dripping"); + const tx = await faucet.drip.mutate({ address: burnerAccount.address }); + console.log("got drip", tx); }; - requestDrip(); - // Request a drip every 20 seconds - setInterval(requestDrip, 20000); + drip(); + setInterval(drip, 20_000); + } catch (e) { + console.error(e); } return { @@ -75,7 +68,7 @@ export async function setupNetwork() { publicClient, walletClient: burnerWalletClient, latestBlock$, - blockStorageOperations$, + storedBlockLogs$, waitForTransaction, worldContract, write$: write$.asObservable().pipe(share()), diff --git a/examples/minimal/packages/client-vanilla/src/index.ts b/examples/minimal/packages/client-vanilla/src/index.ts index fe63aa1950..8666b71a68 100644 --- a/examples/minimal/packages/client-vanilla/src/index.ts +++ b/examples/minimal/packages/client-vanilla/src/index.ts @@ -62,7 +62,7 @@ if (import.meta.env.DEV) { publicClient: network.publicClient, walletClient: network.walletClient, latestBlock$: network.latestBlock$, - blockStorageOperations$: network.blockStorageOperations$, + storedBlockLogs$: network.storedBlockLogs$, worldAddress: network.worldContract.address, worldAbi: network.worldContract.abi, write$: network.write$, diff --git a/examples/minimal/packages/client-vanilla/src/mud/setupNetwork.ts b/examples/minimal/packages/client-vanilla/src/mud/setupNetwork.ts index 85bac97cd8..144ecd4a33 100644 --- a/examples/minimal/packages/client-vanilla/src/mud/setupNetwork.ts +++ b/examples/minimal/packages/client-vanilla/src/mud/setupNetwork.ts @@ -36,7 +36,7 @@ export async function setupNetwork() { onWrite: (write) => write$.next(write), }); - const { components, latestBlock$, blockStorageOperations$, waitForTransaction } = await syncToRecs({ + const { components, latestBlock$, storedBlockLogs$, waitForTransaction } = await syncToRecs({ world, config: mudConfig, address: networkConfig.worldAddress as Hex, @@ -75,7 +75,7 @@ export async function setupNetwork() { publicClient, walletClient: burnerWalletClient, latestBlock$, - blockStorageOperations$, + storedBlockLogs$, waitForTransaction, worldContract, write$: write$.asObservable().pipe(share()), diff --git a/examples/minimal/packages/contracts/.env b/examples/minimal/packages/contracts/.env index 0f8cc4a305..0c925aa484 100644 --- a/examples/minimal/packages/contracts/.env +++ b/examples/minimal/packages/contracts/.env @@ -6,3 +6,5 @@ # # Anvil default private key: PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 +FAUCET_PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 +RPC_HTTP_URL=http://127.0.0.1:8545 diff --git a/examples/minimal/packages/contracts/package.json b/examples/minimal/packages/contracts/package.json index 642f4a17b9..5acf110bed 100644 --- a/examples/minimal/packages/contracts/package.json +++ b/examples/minimal/packages/contracts/package.json @@ -11,6 +11,7 @@ "deploy:local": "pnpm run build && mud deploy", "deploy:testnet": "pnpm run build && mud deploy --profile=lattice-testnet", "dev": "pnpm mud dev-contracts", + "faucet": "DEBUG=mud:faucet pnpm faucet-server", "lint": "pnpm run prettier && pnpm run solhint", "prettier": "prettier --write 'src/**/*.sol'", "solhint": "solhint --config ./.solhint.json 'src/**/*.sol' --fix", @@ -19,6 +20,7 @@ "devDependencies": { "@latticexyz/cli": "link:../../../../packages/cli", "@latticexyz/config": "link:../../../../packages/config", + "@latticexyz/faucet": "link:../../../../packages/faucet", "@latticexyz/schema-type": "link:../../../../packages/schema-type", "@latticexyz/store": "link:../../../../packages/store", "@latticexyz/world": "link:../../../../packages/world", diff --git a/examples/minimal/packages/contracts/src/codegen/tables/CounterTable.sol b/examples/minimal/packages/contracts/src/codegen/tables/CounterTable.sol index ed4f112789..8cff969735 100644 --- a/examples/minimal/packages/contracts/src/codegen/tables/CounterTable.sol +++ b/examples/minimal/packages/contracts/src/codegen/tables/CounterTable.sol @@ -21,13 +21,14 @@ import { PackedCounter, PackedCounterLib } from "@latticexyz/store/src/PackedCou bytes32 constant _tableId = bytes32(abi.encodePacked(bytes16(""), bytes16("CounterTable"))); bytes32 constant CounterTableTableId = _tableId; +FieldLayout constant _fieldLayout = FieldLayout.wrap( + 0x0004010004000000000000000000000000000000000000000000000000000000 +); + library CounterTable { /** Get the table values' field layout */ function getFieldLayout() internal pure returns (FieldLayout) { - uint256[] memory _fieldLayout = new uint256[](1); - _fieldLayout[0] = 4; - - return FieldLayoutLib.encode(_fieldLayout, 0); + return _fieldLayout; } /** Get the table's key schema */ @@ -58,54 +59,77 @@ library CounterTable { /** Register the table with its config */ function register() internal { - StoreSwitch.registerTable( - _tableId, - getFieldLayout(), - getKeySchema(), - getValueSchema(), - getKeyNames(), - getFieldNames() - ); + StoreSwitch.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + } + + /** Register the table with its config */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Register the table with its config (using the specified store) */ function register(IStore _store) internal { - _store.registerTable(_tableId, getFieldLayout(), getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + _store.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Get value */ function get() internal view returns (uint32 value) { bytes32[] memory _keyTuple = new bytes32[](0); - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (uint32(Bytes.slice4(_blob, 0))); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint32(bytes4(_blob))); + } + + /** Get value */ + function _get() internal view returns (uint32 value) { + bytes32[] memory _keyTuple = new bytes32[](0); + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint32(bytes4(_blob))); } /** Get value (using the specified store) */ function get(IStore _store) internal view returns (uint32 value) { bytes32[] memory _keyTuple = new bytes32[](0); - bytes memory _blob = _store.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (uint32(Bytes.slice4(_blob, 0))); + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint32(bytes4(_blob))); } /** Set value */ function set(uint32 value) internal { bytes32[] memory _keyTuple = new bytes32[](0); - StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((value)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); + } + + /** Set value */ + function _set(uint32 value) internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreCore.setField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); } /** Set value (using the specified store) */ function set(IStore _store, uint32 value) internal { bytes32[] memory _keyTuple = new bytes32[](0); - _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((value)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); + } + + /** Tightly pack static data using this table's schema */ + function encodeStatic(uint32 value) internal pure returns (bytes memory) { + return abi.encodePacked(value); } /** Tightly pack full data using this table's field layout */ function encode(uint32 value) internal pure returns (bytes memory) { - return abi.encodePacked(value); + bytes memory _staticData = encodeStatic(value); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; + + return abi.encodePacked(_staticData, _encodedLengths, _dynamicData); } /** Encode keys as a bytes32 array using this table's field layout */ @@ -119,13 +143,20 @@ library CounterTable { function deleteRecord() internal { bytes32[] memory _keyTuple = new bytes32[](0); - StoreSwitch.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + StoreSwitch.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /* Delete all data for given keys */ + function _deleteRecord() internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); } /* Delete all data for given keys (using the specified store) */ function deleteRecord(IStore _store) internal { bytes32[] memory _keyTuple = new bytes32[](0); - _store.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + _store.deleteRecord(_tableId, _keyTuple, _fieldLayout); } } diff --git a/examples/minimal/packages/contracts/src/codegen/tables/Inventory.sol b/examples/minimal/packages/contracts/src/codegen/tables/Inventory.sol index 96df3c93dd..8344c02274 100644 --- a/examples/minimal/packages/contracts/src/codegen/tables/Inventory.sol +++ b/examples/minimal/packages/contracts/src/codegen/tables/Inventory.sol @@ -21,13 +21,14 @@ import { PackedCounter, PackedCounterLib } from "@latticexyz/store/src/PackedCou bytes32 constant _tableId = bytes32(abi.encodePacked(bytes16(""), bytes16("Inventory"))); bytes32 constant InventoryTableId = _tableId; +FieldLayout constant _fieldLayout = FieldLayout.wrap( + 0x0004010004000000000000000000000000000000000000000000000000000000 +); + library Inventory { /** Get the table values' field layout */ function getFieldLayout() internal pure returns (FieldLayout) { - uint256[] memory _fieldLayout = new uint256[](1); - _fieldLayout[0] = 4; - - return FieldLayoutLib.encode(_fieldLayout, 0); + return _fieldLayout; } /** Get the table's key schema */ @@ -64,19 +65,17 @@ library Inventory { /** Register the table with its config */ function register() internal { - StoreSwitch.registerTable( - _tableId, - getFieldLayout(), - getKeySchema(), - getValueSchema(), - getKeyNames(), - getFieldNames() - ); + StoreSwitch.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + } + + /** Register the table with its config */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Register the table with its config (using the specified store) */ function register(IStore _store) internal { - _store.registerTable(_tableId, getFieldLayout(), getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + _store.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Get amount */ @@ -86,8 +85,19 @@ library Inventory { _keyTuple[1] = bytes32(uint256(item)); _keyTuple[2] = bytes32(uint256(itemVariant)); - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (uint32(Bytes.slice4(_blob, 0))); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint32(bytes4(_blob))); + } + + /** Get amount */ + function _get(address owner, uint32 item, uint32 itemVariant) internal view returns (uint32 amount) { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(owner))); + _keyTuple[1] = bytes32(uint256(item)); + _keyTuple[2] = bytes32(uint256(itemVariant)); + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint32(bytes4(_blob))); } /** Get amount (using the specified store) */ @@ -97,8 +107,8 @@ library Inventory { _keyTuple[1] = bytes32(uint256(item)); _keyTuple[2] = bytes32(uint256(itemVariant)); - bytes memory _blob = _store.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (uint32(Bytes.slice4(_blob, 0))); + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint32(bytes4(_blob))); } /** Set amount */ @@ -108,7 +118,17 @@ library Inventory { _keyTuple[1] = bytes32(uint256(item)); _keyTuple[2] = bytes32(uint256(itemVariant)); - StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((amount)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((amount)), _fieldLayout); + } + + /** Set amount */ + function _set(address owner, uint32 item, uint32 itemVariant, uint32 amount) internal { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(owner))); + _keyTuple[1] = bytes32(uint256(item)); + _keyTuple[2] = bytes32(uint256(itemVariant)); + + StoreCore.setField(_tableId, _keyTuple, 0, abi.encodePacked((amount)), _fieldLayout); } /** Set amount (using the specified store) */ @@ -118,12 +138,22 @@ library Inventory { _keyTuple[1] = bytes32(uint256(item)); _keyTuple[2] = bytes32(uint256(itemVariant)); - _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((amount)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((amount)), _fieldLayout); + } + + /** Tightly pack static data using this table's schema */ + function encodeStatic(uint32 amount) internal pure returns (bytes memory) { + return abi.encodePacked(amount); } /** Tightly pack full data using this table's field layout */ function encode(uint32 amount) internal pure returns (bytes memory) { - return abi.encodePacked(amount); + bytes memory _staticData = encodeStatic(amount); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; + + return abi.encodePacked(_staticData, _encodedLengths, _dynamicData); } /** Encode keys as a bytes32 array using this table's field layout */ @@ -143,7 +173,17 @@ library Inventory { _keyTuple[1] = bytes32(uint256(item)); _keyTuple[2] = bytes32(uint256(itemVariant)); - StoreSwitch.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + StoreSwitch.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /* Delete all data for given keys */ + function _deleteRecord(address owner, uint32 item, uint32 itemVariant) internal { + bytes32[] memory _keyTuple = new bytes32[](3); + _keyTuple[0] = bytes32(uint256(uint160(owner))); + _keyTuple[1] = bytes32(uint256(item)); + _keyTuple[2] = bytes32(uint256(itemVariant)); + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); } /* Delete all data for given keys (using the specified store) */ @@ -153,6 +193,6 @@ library Inventory { _keyTuple[1] = bytes32(uint256(item)); _keyTuple[2] = bytes32(uint256(itemVariant)); - _store.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + _store.deleteRecord(_tableId, _keyTuple, _fieldLayout); } } diff --git a/examples/minimal/packages/contracts/src/codegen/tables/MessageTable.sol b/examples/minimal/packages/contracts/src/codegen/tables/MessageTable.sol index 5fe5d13250..9bcba034c6 100644 --- a/examples/minimal/packages/contracts/src/codegen/tables/MessageTable.sol +++ b/examples/minimal/packages/contracts/src/codegen/tables/MessageTable.sol @@ -21,12 +21,14 @@ import { PackedCounter, PackedCounterLib } from "@latticexyz/store/src/PackedCou bytes32 constant _tableId = bytes32(abi.encodePacked(bytes16(""), bytes16("MessageTable"))); bytes32 constant MessageTableTableId = _tableId; +FieldLayout constant _fieldLayout = FieldLayout.wrap( + 0x0000000100000000000000000000000000000000000000000000000000000000 +); + library MessageTable { /** Get the table values' field layout */ function getFieldLayout() internal pure returns (FieldLayout) { - uint256[] memory _fieldLayout = new uint256[](0); - - return FieldLayoutLib.encode(_fieldLayout, 1); + return _fieldLayout; } /** Get the table's key schema */ @@ -57,48 +59,72 @@ library MessageTable { /** Register the table with its config */ function register() internal { - StoreSwitch.registerTable( - _tableId, - getFieldLayout(), - getKeySchema(), - getValueSchema(), - getKeyNames(), - getFieldNames() - ); + StoreSwitch.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + } + + /** Register the table with its config */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Register the table with its config (using the specified store) */ function register(IStore _store) internal { - _store.registerTable(_tableId, getFieldLayout(), getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + _store.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Emit the ephemeral event using individual values */ function emitEphemeral(string memory value) internal { - bytes memory _data = encode(value); + bytes memory _staticData; + PackedCounter _encodedLengths = encodeLengths(value); + bytes memory _dynamicData = encodeDynamic(value); bytes32[] memory _keyTuple = new bytes32[](0); - StoreSwitch.emitEphemeralRecord(_tableId, _keyTuple, _data, getFieldLayout()); + StoreSwitch.emitEphemeralRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); + } + + /** Emit the ephemeral event using individual values */ + function _emitEphemeral(string memory value) internal { + bytes memory _staticData; + PackedCounter _encodedLengths = encodeLengths(value); + bytes memory _dynamicData = encodeDynamic(value); + + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreCore.emitEphemeralRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); } /** Emit the ephemeral event using individual values (using the specified store) */ function emitEphemeral(IStore _store, string memory value) internal { - bytes memory _data = encode(value); + bytes memory _staticData; + PackedCounter _encodedLengths = encodeLengths(value); + bytes memory _dynamicData = encodeDynamic(value); bytes32[] memory _keyTuple = new bytes32[](0); - _store.emitEphemeralRecord(_tableId, _keyTuple, _data, getFieldLayout()); + _store.emitEphemeralRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); } - /** Tightly pack full data using this table's field layout */ - function encode(string memory value) internal pure returns (bytes memory) { - PackedCounter _encodedLengths; + /** Tightly pack dynamic data using this table's schema */ + function encodeLengths(string memory value) internal pure returns (PackedCounter _encodedLengths) { // Lengths are effectively checked during copy by 2**40 bytes exceeding gas limits unchecked { _encodedLengths = PackedCounterLib.pack(bytes(value).length); } + } + + /** Tightly pack dynamic data using this table's schema */ + function encodeDynamic(string memory value) internal pure returns (bytes memory) { + return abi.encodePacked(bytes((value))); + } + + /** Tightly pack full data using this table's field layout */ + function encode(string memory value) internal pure returns (bytes memory) { + bytes memory _staticData; + PackedCounter _encodedLengths = encodeLengths(value); + bytes memory _dynamicData = encodeDynamic(value); - return abi.encodePacked(_encodedLengths.unwrap(), bytes((value))); + return abi.encodePacked(_staticData, _encodedLengths, _dynamicData); } /** Encode keys as a bytes32 array using this table's field layout */ diff --git a/examples/minimal/packages/contracts/test/ChatNamespaced.t.sol b/examples/minimal/packages/contracts/test/ChatNamespaced.t.sol index ac62fff28f..0bd179745f 100644 --- a/examples/minimal/packages/contracts/test/ChatNamespaced.t.sol +++ b/examples/minimal/packages/contracts/test/ChatNamespaced.t.sol @@ -13,8 +13,15 @@ import { IChatNamespacedSystem } from "../src/interfaces/IChatNamespacedSystem.s contract ChatNamespacedTest is MudTest { function testEmitEphemeral() public { bytes32[] memory keyTuple; + string memory value = "test"; vm.expectEmit(true, true, true, true); - emit StoreCore.StoreEphemeralRecord(MessageTableTableId, keyTuple, MessageTable.encode("test")); - IChatNamespacedSystem(worldAddress).namespace_ChatNamespaced_sendMessage("test"); + emit StoreCore.StoreEphemeralRecord( + MessageTableTableId, + keyTuple, + new bytes(0), + MessageTable.encodeLengths(value).unwrap(), + MessageTable.encodeDynamic(value) + ); + IChatNamespacedSystem(worldAddress).namespace_ChatNamespaced_sendMessage(value); } } diff --git a/examples/minimal/pnpm-lock.yaml b/examples/minimal/pnpm-lock.yaml index b0e57b5a9d..7f09b57e02 100644 --- a/examples/minimal/pnpm-lock.yaml +++ b/examples/minimal/pnpm-lock.yaml @@ -147,6 +147,9 @@ importers: '@latticexyz/dev-tools': specifier: link:../../../../packages/dev-tools version: link:../../../../packages/dev-tools + '@latticexyz/faucet': + specifier: link:../../../../packages/faucet + version: link:../../../../packages/faucet '@latticexyz/react': specifier: link:../../../../packages/react version: link:../../../../packages/react @@ -305,6 +308,9 @@ importers: '@latticexyz/config': specifier: link:../../../../packages/config version: link:../../../../packages/config + '@latticexyz/faucet': + specifier: link:../../../../packages/faucet + version: link:../../../../packages/faucet '@latticexyz/schema-type': specifier: link:../../../../packages/schema-type version: link:../../../../packages/schema-type diff --git a/packages/block-logs-stream/src/groupLogsByBlockNumber.ts b/packages/block-logs-stream/src/groupLogsByBlockNumber.ts index e0d2f1144e..b0656acfbc 100644 --- a/packages/block-logs-stream/src/groupLogsByBlockNumber.ts +++ b/packages/block-logs-stream/src/groupLogsByBlockNumber.ts @@ -4,7 +4,7 @@ import { bigIntSort, isDefined } from "@latticexyz/common/utils"; type PartialLog = { blockNumber: bigint; logIndex: number }; export type GroupLogsByBlockNumberResult = { - blockNumber: BlockNumber; + blockNumber: TLog["blockNumber"]; logs: TLog[]; }[]; diff --git a/packages/cli/contracts/src/codegen/tables/Dynamics1.sol b/packages/cli/contracts/src/codegen/tables/Dynamics1.sol index 368c3b73f4..1b7dee5110 100644 --- a/packages/cli/contracts/src/codegen/tables/Dynamics1.sol +++ b/packages/cli/contracts/src/codegen/tables/Dynamics1.sol @@ -21,6 +21,10 @@ import { PackedCounter, PackedCounterLib } from "@latticexyz/store/src/PackedCou bytes32 constant _tableId = bytes32(abi.encodePacked(bytes16(""), bytes16("Dynamics1"))); bytes32 constant Dynamics1TableId = _tableId; +FieldLayout constant _fieldLayout = FieldLayout.wrap( + 0x0000000500000000000000000000000000000000000000000000000000000000 +); + struct Dynamics1Data { bytes32[1] staticB32; int32[2] staticI32; @@ -32,9 +36,7 @@ struct Dynamics1Data { library Dynamics1 { /** Get the table values' field layout */ function getFieldLayout() internal pure returns (FieldLayout) { - uint256[] memory _fieldLayout = new uint256[](0); - - return FieldLayoutLib.encode(_fieldLayout, 5); + return _fieldLayout; } /** Get the table's key schema */ @@ -75,19 +77,17 @@ library Dynamics1 { /** Register the table with its config */ function register() internal { - StoreSwitch.registerTable( - _tableId, - getFieldLayout(), - getKeySchema(), - getValueSchema(), - getKeyNames(), - getFieldNames() - ); + StoreSwitch.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + } + + /** Register the table with its config */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Register the table with its config (using the specified store) */ function register(IStore _store) internal { - _store.registerTable(_tableId, getFieldLayout(), getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + _store.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Get staticB32 */ @@ -95,7 +95,16 @@ library Dynamics1 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 0, getFieldLayout()); + bytes memory _blob = StoreSwitch.getDynamicField(_tableId, _keyTuple, 0); + return toStaticArray_bytes32_1(SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_bytes32()); + } + + /** Get staticB32 */ + function _getStaticB32(bytes32 key) internal view returns (bytes32[1] memory staticB32) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + bytes memory _blob = StoreCore.getDynamicField(_tableId, _keyTuple, 0); return toStaticArray_bytes32_1(SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_bytes32()); } @@ -104,7 +113,7 @@ library Dynamics1 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = _store.getField(_tableId, _keyTuple, 0, getFieldLayout()); + bytes memory _blob = _store.getDynamicField(_tableId, _keyTuple, 0); return toStaticArray_bytes32_1(SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_bytes32()); } @@ -118,16 +127,24 @@ library Dynamics1 { _keyTuple, 0, EncodeArray.encode(fromStaticArray_bytes32_1(staticB32)), - getFieldLayout() + _fieldLayout ); } + /** Set staticB32 */ + function _setStaticB32(bytes32 key, bytes32[1] memory staticB32) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.setField(_tableId, _keyTuple, 0, EncodeArray.encode(fromStaticArray_bytes32_1(staticB32)), _fieldLayout); + } + /** Set staticB32 (using the specified store) */ function setStaticB32(IStore _store, bytes32 key, bytes32[1] memory staticB32) internal { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.setField(_tableId, _keyTuple, 0, EncodeArray.encode(fromStaticArray_bytes32_1(staticB32)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 0, EncodeArray.encode(fromStaticArray_bytes32_1(staticB32)), _fieldLayout); } /** Get the length of staticB32 */ @@ -135,7 +152,18 @@ library Dynamics1 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 0, getFieldLayout()); + uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 0, _fieldLayout); + unchecked { + return _byteLength / 32; + } + } + + /** Get the length of staticB32 */ + function _lengthStaticB32(bytes32 key) internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + uint256 _byteLength = StoreCore.getFieldLength(_tableId, _keyTuple, 0, _fieldLayout); unchecked { return _byteLength / 32; } @@ -146,7 +174,7 @@ library Dynamics1 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 0, getFieldLayout()); + uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 0, _fieldLayout); unchecked { return _byteLength / 32; } @@ -165,32 +193,46 @@ library Dynamics1 { _tableId, _keyTuple, 0, - getFieldLayout(), + _fieldLayout, _index * 32, (_index + 1) * 32 ); - return (Bytes.slice32(_blob, 0)); + return (bytes32(_blob)); } } /** - * Get an item of staticB32 (using the specified store) + * Get an item of staticB32 * (unchecked, returns invalid data if index overflows) */ - function getItemStaticB32(IStore _store, bytes32 key, uint256 _index) internal view returns (bytes32) { + function _getItemStaticB32(bytes32 key, uint256 _index) internal view returns (bytes32) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; unchecked { - bytes memory _blob = _store.getFieldSlice( + bytes memory _blob = StoreCore.getFieldSlice( _tableId, _keyTuple, 0, - getFieldLayout(), + _fieldLayout, _index * 32, (_index + 1) * 32 ); - return (Bytes.slice32(_blob, 0)); + return (bytes32(_blob)); + } + } + + /** + * Get an item of staticB32 (using the specified store) + * (unchecked, returns invalid data if index overflows) + */ + function getItemStaticB32(IStore _store, bytes32 key, uint256 _index) internal view returns (bytes32) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + unchecked { + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 0, _fieldLayout, _index * 32, (_index + 1) * 32); + return (bytes32(_blob)); } } @@ -199,7 +241,15 @@ library Dynamics1 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), getFieldLayout()); + StoreSwitch.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), _fieldLayout); + } + + /** Push an element to staticB32 */ + function _pushStaticB32(bytes32 key, bytes32 _element) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), _fieldLayout); } /** Push an element to staticB32 (using the specified store) */ @@ -207,7 +257,7 @@ library Dynamics1 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), getFieldLayout()); + _store.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), _fieldLayout); } /** Pop an element from staticB32 */ @@ -215,7 +265,15 @@ library Dynamics1 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.popFromField(_tableId, _keyTuple, 0, 32, getFieldLayout()); + StoreSwitch.popFromField(_tableId, _keyTuple, 0, 32, _fieldLayout); + } + + /** Pop an element from staticB32 */ + function _popStaticB32(bytes32 key) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.popFromField(_tableId, _keyTuple, 0, 32, _fieldLayout); } /** Pop an element from staticB32 (using the specified store) */ @@ -223,7 +281,7 @@ library Dynamics1 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.popFromField(_tableId, _keyTuple, 0, 32, getFieldLayout()); + _store.popFromField(_tableId, _keyTuple, 0, 32, _fieldLayout); } /** @@ -235,7 +293,20 @@ library Dynamics1 { _keyTuple[0] = key; unchecked { - StoreSwitch.updateInField(_tableId, _keyTuple, 0, _index * 32, abi.encodePacked((_element)), getFieldLayout()); + StoreSwitch.updateInField(_tableId, _keyTuple, 0, _index * 32, abi.encodePacked((_element)), _fieldLayout); + } + } + + /** + * Update an element of staticB32 at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ + function _updateStaticB32(bytes32 key, uint256 _index, bytes32 _element) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + unchecked { + StoreCore.updateInField(_tableId, _keyTuple, 0, _index * 32, abi.encodePacked((_element)), _fieldLayout); } } @@ -248,7 +319,7 @@ library Dynamics1 { _keyTuple[0] = key; unchecked { - _store.updateInField(_tableId, _keyTuple, 0, _index * 32, abi.encodePacked((_element)), getFieldLayout()); + _store.updateInField(_tableId, _keyTuple, 0, _index * 32, abi.encodePacked((_element)), _fieldLayout); } } @@ -257,7 +328,16 @@ library Dynamics1 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 1, getFieldLayout()); + bytes memory _blob = StoreSwitch.getDynamicField(_tableId, _keyTuple, 1); + return toStaticArray_int32_2(SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_int32()); + } + + /** Get staticI32 */ + function _getStaticI32(bytes32 key) internal view returns (int32[2] memory staticI32) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + bytes memory _blob = StoreCore.getDynamicField(_tableId, _keyTuple, 1); return toStaticArray_int32_2(SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_int32()); } @@ -266,7 +346,7 @@ library Dynamics1 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = _store.getField(_tableId, _keyTuple, 1, getFieldLayout()); + bytes memory _blob = _store.getDynamicField(_tableId, _keyTuple, 1); return toStaticArray_int32_2(SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_int32()); } @@ -275,13 +355,15 @@ library Dynamics1 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.setField( - _tableId, - _keyTuple, - 1, - EncodeArray.encode(fromStaticArray_int32_2(staticI32)), - getFieldLayout() - ); + StoreSwitch.setField(_tableId, _keyTuple, 1, EncodeArray.encode(fromStaticArray_int32_2(staticI32)), _fieldLayout); + } + + /** Set staticI32 */ + function _setStaticI32(bytes32 key, int32[2] memory staticI32) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.setField(_tableId, _keyTuple, 1, EncodeArray.encode(fromStaticArray_int32_2(staticI32)), _fieldLayout); } /** Set staticI32 (using the specified store) */ @@ -289,7 +371,7 @@ library Dynamics1 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.setField(_tableId, _keyTuple, 1, EncodeArray.encode(fromStaticArray_int32_2(staticI32)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 1, EncodeArray.encode(fromStaticArray_int32_2(staticI32)), _fieldLayout); } /** Get the length of staticI32 */ @@ -297,7 +379,18 @@ library Dynamics1 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 1, getFieldLayout()); + uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 1, _fieldLayout); + unchecked { + return _byteLength / 4; + } + } + + /** Get the length of staticI32 */ + function _lengthStaticI32(bytes32 key) internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + uint256 _byteLength = StoreCore.getFieldLength(_tableId, _keyTuple, 1, _fieldLayout); unchecked { return _byteLength / 4; } @@ -308,7 +401,7 @@ library Dynamics1 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 1, getFieldLayout()); + uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 1, _fieldLayout); unchecked { return _byteLength / 4; } @@ -327,11 +420,25 @@ library Dynamics1 { _tableId, _keyTuple, 1, - getFieldLayout(), + _fieldLayout, _index * 4, (_index + 1) * 4 ); - return (int32(uint32(Bytes.slice4(_blob, 0)))); + return (int32(uint32(bytes4(_blob)))); + } + } + + /** + * Get an item of staticI32 + * (unchecked, returns invalid data if index overflows) + */ + function _getItemStaticI32(bytes32 key, uint256 _index) internal view returns (int32) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + unchecked { + bytes memory _blob = StoreCore.getFieldSlice(_tableId, _keyTuple, 1, _fieldLayout, _index * 4, (_index + 1) * 4); + return (int32(uint32(bytes4(_blob)))); } } @@ -344,8 +451,8 @@ library Dynamics1 { _keyTuple[0] = key; unchecked { - bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 1, getFieldLayout(), _index * 4, (_index + 1) * 4); - return (int32(uint32(Bytes.slice4(_blob, 0)))); + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 1, _fieldLayout, _index * 4, (_index + 1) * 4); + return (int32(uint32(bytes4(_blob)))); } } @@ -354,7 +461,15 @@ library Dynamics1 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.pushToField(_tableId, _keyTuple, 1, abi.encodePacked((_element)), getFieldLayout()); + StoreSwitch.pushToField(_tableId, _keyTuple, 1, abi.encodePacked((_element)), _fieldLayout); + } + + /** Push an element to staticI32 */ + function _pushStaticI32(bytes32 key, int32 _element) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.pushToField(_tableId, _keyTuple, 1, abi.encodePacked((_element)), _fieldLayout); } /** Push an element to staticI32 (using the specified store) */ @@ -362,7 +477,7 @@ library Dynamics1 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.pushToField(_tableId, _keyTuple, 1, abi.encodePacked((_element)), getFieldLayout()); + _store.pushToField(_tableId, _keyTuple, 1, abi.encodePacked((_element)), _fieldLayout); } /** Pop an element from staticI32 */ @@ -370,7 +485,15 @@ library Dynamics1 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.popFromField(_tableId, _keyTuple, 1, 4, getFieldLayout()); + StoreSwitch.popFromField(_tableId, _keyTuple, 1, 4, _fieldLayout); + } + + /** Pop an element from staticI32 */ + function _popStaticI32(bytes32 key) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.popFromField(_tableId, _keyTuple, 1, 4, _fieldLayout); } /** Pop an element from staticI32 (using the specified store) */ @@ -378,7 +501,7 @@ library Dynamics1 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.popFromField(_tableId, _keyTuple, 1, 4, getFieldLayout()); + _store.popFromField(_tableId, _keyTuple, 1, 4, _fieldLayout); } /** @@ -390,7 +513,20 @@ library Dynamics1 { _keyTuple[0] = key; unchecked { - StoreSwitch.updateInField(_tableId, _keyTuple, 1, _index * 4, abi.encodePacked((_element)), getFieldLayout()); + StoreSwitch.updateInField(_tableId, _keyTuple, 1, _index * 4, abi.encodePacked((_element)), _fieldLayout); + } + } + + /** + * Update an element of staticI32 at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ + function _updateStaticI32(bytes32 key, uint256 _index, int32 _element) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + unchecked { + StoreCore.updateInField(_tableId, _keyTuple, 1, _index * 4, abi.encodePacked((_element)), _fieldLayout); } } @@ -403,7 +539,7 @@ library Dynamics1 { _keyTuple[0] = key; unchecked { - _store.updateInField(_tableId, _keyTuple, 1, _index * 4, abi.encodePacked((_element)), getFieldLayout()); + _store.updateInField(_tableId, _keyTuple, 1, _index * 4, abi.encodePacked((_element)), _fieldLayout); } } @@ -412,7 +548,16 @@ library Dynamics1 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 2, getFieldLayout()); + bytes memory _blob = StoreSwitch.getDynamicField(_tableId, _keyTuple, 2); + return toStaticArray_uint128_3(SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_uint128()); + } + + /** Get staticU128 */ + function _getStaticU128(bytes32 key) internal view returns (uint128[3] memory staticU128) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + bytes memory _blob = StoreCore.getDynamicField(_tableId, _keyTuple, 2); return toStaticArray_uint128_3(SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_uint128()); } @@ -421,7 +566,7 @@ library Dynamics1 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = _store.getField(_tableId, _keyTuple, 2, getFieldLayout()); + bytes memory _blob = _store.getDynamicField(_tableId, _keyTuple, 2); return toStaticArray_uint128_3(SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_uint128()); } @@ -435,22 +580,24 @@ library Dynamics1 { _keyTuple, 2, EncodeArray.encode(fromStaticArray_uint128_3(staticU128)), - getFieldLayout() + _fieldLayout ); } + /** Set staticU128 */ + function _setStaticU128(bytes32 key, uint128[3] memory staticU128) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.setField(_tableId, _keyTuple, 2, EncodeArray.encode(fromStaticArray_uint128_3(staticU128)), _fieldLayout); + } + /** Set staticU128 (using the specified store) */ function setStaticU128(IStore _store, bytes32 key, uint128[3] memory staticU128) internal { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.setField( - _tableId, - _keyTuple, - 2, - EncodeArray.encode(fromStaticArray_uint128_3(staticU128)), - getFieldLayout() - ); + _store.setField(_tableId, _keyTuple, 2, EncodeArray.encode(fromStaticArray_uint128_3(staticU128)), _fieldLayout); } /** Get the length of staticU128 */ @@ -458,7 +605,18 @@ library Dynamics1 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 2, getFieldLayout()); + uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 2, _fieldLayout); + unchecked { + return _byteLength / 16; + } + } + + /** Get the length of staticU128 */ + function _lengthStaticU128(bytes32 key) internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + uint256 _byteLength = StoreCore.getFieldLength(_tableId, _keyTuple, 2, _fieldLayout); unchecked { return _byteLength / 16; } @@ -469,7 +627,7 @@ library Dynamics1 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 2, getFieldLayout()); + uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 2, _fieldLayout); unchecked { return _byteLength / 16; } @@ -488,32 +646,46 @@ library Dynamics1 { _tableId, _keyTuple, 2, - getFieldLayout(), + _fieldLayout, _index * 16, (_index + 1) * 16 ); - return (uint128(Bytes.slice16(_blob, 0))); + return (uint128(bytes16(_blob))); } } /** - * Get an item of staticU128 (using the specified store) + * Get an item of staticU128 * (unchecked, returns invalid data if index overflows) */ - function getItemStaticU128(IStore _store, bytes32 key, uint256 _index) internal view returns (uint128) { + function _getItemStaticU128(bytes32 key, uint256 _index) internal view returns (uint128) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; unchecked { - bytes memory _blob = _store.getFieldSlice( + bytes memory _blob = StoreCore.getFieldSlice( _tableId, _keyTuple, 2, - getFieldLayout(), + _fieldLayout, _index * 16, (_index + 1) * 16 ); - return (uint128(Bytes.slice16(_blob, 0))); + return (uint128(bytes16(_blob))); + } + } + + /** + * Get an item of staticU128 (using the specified store) + * (unchecked, returns invalid data if index overflows) + */ + function getItemStaticU128(IStore _store, bytes32 key, uint256 _index) internal view returns (uint128) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + unchecked { + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 2, _fieldLayout, _index * 16, (_index + 1) * 16); + return (uint128(bytes16(_blob))); } } @@ -522,7 +694,15 @@ library Dynamics1 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.pushToField(_tableId, _keyTuple, 2, abi.encodePacked((_element)), getFieldLayout()); + StoreSwitch.pushToField(_tableId, _keyTuple, 2, abi.encodePacked((_element)), _fieldLayout); + } + + /** Push an element to staticU128 */ + function _pushStaticU128(bytes32 key, uint128 _element) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.pushToField(_tableId, _keyTuple, 2, abi.encodePacked((_element)), _fieldLayout); } /** Push an element to staticU128 (using the specified store) */ @@ -530,7 +710,7 @@ library Dynamics1 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.pushToField(_tableId, _keyTuple, 2, abi.encodePacked((_element)), getFieldLayout()); + _store.pushToField(_tableId, _keyTuple, 2, abi.encodePacked((_element)), _fieldLayout); } /** Pop an element from staticU128 */ @@ -538,7 +718,15 @@ library Dynamics1 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.popFromField(_tableId, _keyTuple, 2, 16, getFieldLayout()); + StoreSwitch.popFromField(_tableId, _keyTuple, 2, 16, _fieldLayout); + } + + /** Pop an element from staticU128 */ + function _popStaticU128(bytes32 key) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.popFromField(_tableId, _keyTuple, 2, 16, _fieldLayout); } /** Pop an element from staticU128 (using the specified store) */ @@ -546,7 +734,7 @@ library Dynamics1 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.popFromField(_tableId, _keyTuple, 2, 16, getFieldLayout()); + _store.popFromField(_tableId, _keyTuple, 2, 16, _fieldLayout); } /** @@ -558,7 +746,20 @@ library Dynamics1 { _keyTuple[0] = key; unchecked { - StoreSwitch.updateInField(_tableId, _keyTuple, 2, _index * 16, abi.encodePacked((_element)), getFieldLayout()); + StoreSwitch.updateInField(_tableId, _keyTuple, 2, _index * 16, abi.encodePacked((_element)), _fieldLayout); + } + } + + /** + * Update an element of staticU128 at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ + function _updateStaticU128(bytes32 key, uint256 _index, uint128 _element) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + unchecked { + StoreCore.updateInField(_tableId, _keyTuple, 2, _index * 16, abi.encodePacked((_element)), _fieldLayout); } } @@ -571,7 +772,7 @@ library Dynamics1 { _keyTuple[0] = key; unchecked { - _store.updateInField(_tableId, _keyTuple, 2, _index * 16, abi.encodePacked((_element)), getFieldLayout()); + _store.updateInField(_tableId, _keyTuple, 2, _index * 16, abi.encodePacked((_element)), _fieldLayout); } } @@ -580,7 +781,16 @@ library Dynamics1 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 3, getFieldLayout()); + bytes memory _blob = StoreSwitch.getDynamicField(_tableId, _keyTuple, 3); + return toStaticArray_address_4(SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_address()); + } + + /** Get staticAddrs */ + function _getStaticAddrs(bytes32 key) internal view returns (address[4] memory staticAddrs) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + bytes memory _blob = StoreCore.getDynamicField(_tableId, _keyTuple, 3); return toStaticArray_address_4(SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_address()); } @@ -589,7 +799,7 @@ library Dynamics1 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = _store.getField(_tableId, _keyTuple, 3, getFieldLayout()); + bytes memory _blob = _store.getDynamicField(_tableId, _keyTuple, 3); return toStaticArray_address_4(SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_address()); } @@ -603,30 +813,49 @@ library Dynamics1 { _keyTuple, 3, EncodeArray.encode(fromStaticArray_address_4(staticAddrs)), - getFieldLayout() + _fieldLayout ); } - /** Set staticAddrs (using the specified store) */ - function setStaticAddrs(IStore _store, bytes32 key, address[4] memory staticAddrs) internal { + /** Set staticAddrs */ + function _setStaticAddrs(bytes32 key, address[4] memory staticAddrs) internal { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.setField( + StoreCore.setField( _tableId, _keyTuple, 3, EncodeArray.encode(fromStaticArray_address_4(staticAddrs)), - getFieldLayout() + _fieldLayout ); } + /** Set staticAddrs (using the specified store) */ + function setStaticAddrs(IStore _store, bytes32 key, address[4] memory staticAddrs) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + _store.setField(_tableId, _keyTuple, 3, EncodeArray.encode(fromStaticArray_address_4(staticAddrs)), _fieldLayout); + } + /** Get the length of staticAddrs */ function lengthStaticAddrs(bytes32 key) internal view returns (uint256) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 3, getFieldLayout()); + uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 3, _fieldLayout); + unchecked { + return _byteLength / 20; + } + } + + /** Get the length of staticAddrs */ + function _lengthStaticAddrs(bytes32 key) internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + uint256 _byteLength = StoreCore.getFieldLength(_tableId, _keyTuple, 3, _fieldLayout); unchecked { return _byteLength / 20; } @@ -637,7 +866,7 @@ library Dynamics1 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 3, getFieldLayout()); + uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 3, _fieldLayout); unchecked { return _byteLength / 20; } @@ -656,32 +885,46 @@ library Dynamics1 { _tableId, _keyTuple, 3, - getFieldLayout(), + _fieldLayout, _index * 20, (_index + 1) * 20 ); - return (address(Bytes.slice20(_blob, 0))); + return (address(bytes20(_blob))); } } /** - * Get an item of staticAddrs (using the specified store) + * Get an item of staticAddrs * (unchecked, returns invalid data if index overflows) */ - function getItemStaticAddrs(IStore _store, bytes32 key, uint256 _index) internal view returns (address) { + function _getItemStaticAddrs(bytes32 key, uint256 _index) internal view returns (address) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; unchecked { - bytes memory _blob = _store.getFieldSlice( + bytes memory _blob = StoreCore.getFieldSlice( _tableId, _keyTuple, 3, - getFieldLayout(), + _fieldLayout, _index * 20, (_index + 1) * 20 ); - return (address(Bytes.slice20(_blob, 0))); + return (address(bytes20(_blob))); + } + } + + /** + * Get an item of staticAddrs (using the specified store) + * (unchecked, returns invalid data if index overflows) + */ + function getItemStaticAddrs(IStore _store, bytes32 key, uint256 _index) internal view returns (address) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + unchecked { + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 3, _fieldLayout, _index * 20, (_index + 1) * 20); + return (address(bytes20(_blob))); } } @@ -690,7 +933,15 @@ library Dynamics1 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.pushToField(_tableId, _keyTuple, 3, abi.encodePacked((_element)), getFieldLayout()); + StoreSwitch.pushToField(_tableId, _keyTuple, 3, abi.encodePacked((_element)), _fieldLayout); + } + + /** Push an element to staticAddrs */ + function _pushStaticAddrs(bytes32 key, address _element) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.pushToField(_tableId, _keyTuple, 3, abi.encodePacked((_element)), _fieldLayout); } /** Push an element to staticAddrs (using the specified store) */ @@ -698,7 +949,7 @@ library Dynamics1 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.pushToField(_tableId, _keyTuple, 3, abi.encodePacked((_element)), getFieldLayout()); + _store.pushToField(_tableId, _keyTuple, 3, abi.encodePacked((_element)), _fieldLayout); } /** Pop an element from staticAddrs */ @@ -706,7 +957,15 @@ library Dynamics1 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.popFromField(_tableId, _keyTuple, 3, 20, getFieldLayout()); + StoreSwitch.popFromField(_tableId, _keyTuple, 3, 20, _fieldLayout); + } + + /** Pop an element from staticAddrs */ + function _popStaticAddrs(bytes32 key) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.popFromField(_tableId, _keyTuple, 3, 20, _fieldLayout); } /** Pop an element from staticAddrs (using the specified store) */ @@ -714,7 +973,7 @@ library Dynamics1 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.popFromField(_tableId, _keyTuple, 3, 20, getFieldLayout()); + _store.popFromField(_tableId, _keyTuple, 3, 20, _fieldLayout); } /** @@ -726,7 +985,20 @@ library Dynamics1 { _keyTuple[0] = key; unchecked { - StoreSwitch.updateInField(_tableId, _keyTuple, 3, _index * 20, abi.encodePacked((_element)), getFieldLayout()); + StoreSwitch.updateInField(_tableId, _keyTuple, 3, _index * 20, abi.encodePacked((_element)), _fieldLayout); + } + } + + /** + * Update an element of staticAddrs at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ + function _updateStaticAddrs(bytes32 key, uint256 _index, address _element) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + unchecked { + StoreCore.updateInField(_tableId, _keyTuple, 3, _index * 20, abi.encodePacked((_element)), _fieldLayout); } } @@ -739,7 +1011,7 @@ library Dynamics1 { _keyTuple[0] = key; unchecked { - _store.updateInField(_tableId, _keyTuple, 3, _index * 20, abi.encodePacked((_element)), getFieldLayout()); + _store.updateInField(_tableId, _keyTuple, 3, _index * 20, abi.encodePacked((_element)), _fieldLayout); } } @@ -748,7 +1020,16 @@ library Dynamics1 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 4, getFieldLayout()); + bytes memory _blob = StoreSwitch.getDynamicField(_tableId, _keyTuple, 4); + return toStaticArray_bool_5(SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_bool()); + } + + /** Get staticBools */ + function _getStaticBools(bytes32 key) internal view returns (bool[5] memory staticBools) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + bytes memory _blob = StoreCore.getDynamicField(_tableId, _keyTuple, 4); return toStaticArray_bool_5(SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_bool()); } @@ -757,7 +1038,7 @@ library Dynamics1 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = _store.getField(_tableId, _keyTuple, 4, getFieldLayout()); + bytes memory _blob = _store.getDynamicField(_tableId, _keyTuple, 4); return toStaticArray_bool_5(SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_bool()); } @@ -766,13 +1047,15 @@ library Dynamics1 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.setField( - _tableId, - _keyTuple, - 4, - EncodeArray.encode(fromStaticArray_bool_5(staticBools)), - getFieldLayout() - ); + StoreSwitch.setField(_tableId, _keyTuple, 4, EncodeArray.encode(fromStaticArray_bool_5(staticBools)), _fieldLayout); + } + + /** Set staticBools */ + function _setStaticBools(bytes32 key, bool[5] memory staticBools) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.setField(_tableId, _keyTuple, 4, EncodeArray.encode(fromStaticArray_bool_5(staticBools)), _fieldLayout); } /** Set staticBools (using the specified store) */ @@ -780,7 +1063,7 @@ library Dynamics1 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.setField(_tableId, _keyTuple, 4, EncodeArray.encode(fromStaticArray_bool_5(staticBools)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 4, EncodeArray.encode(fromStaticArray_bool_5(staticBools)), _fieldLayout); } /** Get the length of staticBools */ @@ -788,7 +1071,18 @@ library Dynamics1 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 4, getFieldLayout()); + uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 4, _fieldLayout); + unchecked { + return _byteLength / 1; + } + } + + /** Get the length of staticBools */ + function _lengthStaticBools(bytes32 key) internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + uint256 _byteLength = StoreCore.getFieldLength(_tableId, _keyTuple, 4, _fieldLayout); unchecked { return _byteLength / 1; } @@ -799,7 +1093,7 @@ library Dynamics1 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 4, getFieldLayout()); + uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 4, _fieldLayout); unchecked { return _byteLength / 1; } @@ -818,11 +1112,25 @@ library Dynamics1 { _tableId, _keyTuple, 4, - getFieldLayout(), + _fieldLayout, _index * 1, (_index + 1) * 1 ); - return (_toBool(uint8(Bytes.slice1(_blob, 0)))); + return (_toBool(uint8(bytes1(_blob)))); + } + } + + /** + * Get an item of staticBools + * (unchecked, returns invalid data if index overflows) + */ + function _getItemStaticBools(bytes32 key, uint256 _index) internal view returns (bool) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + unchecked { + bytes memory _blob = StoreCore.getFieldSlice(_tableId, _keyTuple, 4, _fieldLayout, _index * 1, (_index + 1) * 1); + return (_toBool(uint8(bytes1(_blob)))); } } @@ -835,8 +1143,8 @@ library Dynamics1 { _keyTuple[0] = key; unchecked { - bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 4, getFieldLayout(), _index * 1, (_index + 1) * 1); - return (_toBool(uint8(Bytes.slice1(_blob, 0)))); + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 4, _fieldLayout, _index * 1, (_index + 1) * 1); + return (_toBool(uint8(bytes1(_blob)))); } } @@ -845,7 +1153,15 @@ library Dynamics1 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.pushToField(_tableId, _keyTuple, 4, abi.encodePacked((_element)), getFieldLayout()); + StoreSwitch.pushToField(_tableId, _keyTuple, 4, abi.encodePacked((_element)), _fieldLayout); + } + + /** Push an element to staticBools */ + function _pushStaticBools(bytes32 key, bool _element) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.pushToField(_tableId, _keyTuple, 4, abi.encodePacked((_element)), _fieldLayout); } /** Push an element to staticBools (using the specified store) */ @@ -853,7 +1169,7 @@ library Dynamics1 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.pushToField(_tableId, _keyTuple, 4, abi.encodePacked((_element)), getFieldLayout()); + _store.pushToField(_tableId, _keyTuple, 4, abi.encodePacked((_element)), _fieldLayout); } /** Pop an element from staticBools */ @@ -861,7 +1177,15 @@ library Dynamics1 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.popFromField(_tableId, _keyTuple, 4, 1, getFieldLayout()); + StoreSwitch.popFromField(_tableId, _keyTuple, 4, 1, _fieldLayout); + } + + /** Pop an element from staticBools */ + function _popStaticBools(bytes32 key) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.popFromField(_tableId, _keyTuple, 4, 1, _fieldLayout); } /** Pop an element from staticBools (using the specified store) */ @@ -869,7 +1193,7 @@ library Dynamics1 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.popFromField(_tableId, _keyTuple, 4, 1, getFieldLayout()); + _store.popFromField(_tableId, _keyTuple, 4, 1, _fieldLayout); } /** @@ -881,7 +1205,20 @@ library Dynamics1 { _keyTuple[0] = key; unchecked { - StoreSwitch.updateInField(_tableId, _keyTuple, 4, _index * 1, abi.encodePacked((_element)), getFieldLayout()); + StoreSwitch.updateInField(_tableId, _keyTuple, 4, _index * 1, abi.encodePacked((_element)), _fieldLayout); + } + } + + /** + * Update an element of staticBools at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ + function _updateStaticBools(bytes32 key, uint256 _index, bool _element) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + unchecked { + StoreCore.updateInField(_tableId, _keyTuple, 4, _index * 1, abi.encodePacked((_element)), _fieldLayout); } } @@ -894,7 +1231,7 @@ library Dynamics1 { _keyTuple[0] = key; unchecked { - _store.updateInField(_tableId, _keyTuple, 4, _index * 1, abi.encodePacked((_element)), getFieldLayout()); + _store.updateInField(_tableId, _keyTuple, 4, _index * 1, abi.encodePacked((_element)), _fieldLayout); } } @@ -903,7 +1240,16 @@ library Dynamics1 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = StoreSwitch.getRecord(_tableId, _keyTuple, getFieldLayout()); + bytes memory _blob = StoreSwitch.getRecord(_tableId, _keyTuple, _fieldLayout); + return decode(_blob); + } + + /** Get the full data */ + function _get(bytes32 key) internal view returns (Dynamics1Data memory _table) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + bytes memory _blob = StoreCore.getRecord(_tableId, _keyTuple, _fieldLayout); return decode(_blob); } @@ -912,7 +1258,7 @@ library Dynamics1 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = _store.getRecord(_tableId, _keyTuple, getFieldLayout()); + bytes memory _blob = _store.getRecord(_tableId, _keyTuple, _fieldLayout); return decode(_blob); } @@ -925,12 +1271,33 @@ library Dynamics1 { address[4] memory staticAddrs, bool[5] memory staticBools ) internal { - bytes memory _data = encode(staticB32, staticI32, staticU128, staticAddrs, staticBools); + bytes memory _staticData; + PackedCounter _encodedLengths = encodeLengths(staticB32, staticI32, staticU128, staticAddrs, staticBools); + bytes memory _dynamicData = encodeDynamic(staticB32, staticI32, staticU128, staticAddrs, staticBools); bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.setRecord(_tableId, _keyTuple, _data, getFieldLayout()); + StoreSwitch.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); + } + + /** Set the full data using individual values */ + function _set( + bytes32 key, + bytes32[1] memory staticB32, + int32[2] memory staticI32, + uint128[3] memory staticU128, + address[4] memory staticAddrs, + bool[5] memory staticBools + ) internal { + bytes memory _staticData; + PackedCounter _encodedLengths = encodeLengths(staticB32, staticI32, staticU128, staticAddrs, staticBools); + bytes memory _dynamicData = encodeDynamic(staticB32, staticI32, staticU128, staticAddrs, staticBools); + + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); } /** Set the full data using individual values (using the specified store) */ @@ -943,12 +1310,14 @@ library Dynamics1 { address[4] memory staticAddrs, bool[5] memory staticBools ) internal { - bytes memory _data = encode(staticB32, staticI32, staticU128, staticAddrs, staticBools); + bytes memory _staticData; + PackedCounter _encodedLengths = encodeLengths(staticB32, staticI32, staticU128, staticAddrs, staticBools); + bytes memory _dynamicData = encodeDynamic(staticB32, staticI32, staticU128, staticAddrs, staticBools); bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.setRecord(_tableId, _keyTuple, _data, getFieldLayout()); + _store.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); } /** Set the full data using the data struct */ @@ -956,6 +1325,11 @@ library Dynamics1 { set(key, _table.staticB32, _table.staticI32, _table.staticU128, _table.staticAddrs, _table.staticBools); } + /** Set the full data using the data struct */ + function _set(bytes32 key, Dynamics1Data memory _table) internal { + set(key, _table.staticB32, _table.staticI32, _table.staticU128, _table.staticAddrs, _table.staticBools); + } + /** Set the full data using the data struct (using the specified store) */ function set(IStore _store, bytes32 key, Dynamics1Data memory _table) internal { set(_store, key, _table.staticB32, _table.staticI32, _table.staticU128, _table.staticAddrs, _table.staticBools); @@ -1005,15 +1379,14 @@ library Dynamics1 { } } - /** Tightly pack full data using this table's field layout */ - function encode( + /** Tightly pack dynamic data using this table's schema */ + function encodeLengths( bytes32[1] memory staticB32, int32[2] memory staticI32, uint128[3] memory staticU128, address[4] memory staticAddrs, bool[5] memory staticBools - ) internal pure returns (bytes memory) { - PackedCounter _encodedLengths; + ) internal pure returns (PackedCounter _encodedLengths) { // Lengths are effectively checked during copy by 2**40 bytes exceeding gas limits unchecked { _encodedLengths = PackedCounterLib.pack( @@ -1024,10 +1397,18 @@ library Dynamics1 { staticBools.length * 1 ); } + } + /** Tightly pack dynamic data using this table's schema */ + function encodeDynamic( + bytes32[1] memory staticB32, + int32[2] memory staticI32, + uint128[3] memory staticU128, + address[4] memory staticAddrs, + bool[5] memory staticBools + ) internal pure returns (bytes memory) { return abi.encodePacked( - _encodedLengths.unwrap(), EncodeArray.encode(fromStaticArray_bytes32_1(staticB32)), EncodeArray.encode(fromStaticArray_int32_2(staticI32)), EncodeArray.encode(fromStaticArray_uint128_3(staticU128)), @@ -1036,6 +1417,21 @@ library Dynamics1 { ); } + /** Tightly pack full data using this table's field layout */ + function encode( + bytes32[1] memory staticB32, + int32[2] memory staticI32, + uint128[3] memory staticU128, + address[4] memory staticAddrs, + bool[5] memory staticBools + ) internal pure returns (bytes memory) { + bytes memory _staticData; + PackedCounter _encodedLengths = encodeLengths(staticB32, staticI32, staticU128, staticAddrs, staticBools); + bytes memory _dynamicData = encodeDynamic(staticB32, staticI32, staticU128, staticAddrs, staticBools); + + return abi.encodePacked(_staticData, _encodedLengths, _dynamicData); + } + /** Encode keys as a bytes32 array using this table's field layout */ function encodeKeyTuple(bytes32 key) internal pure returns (bytes32[] memory) { bytes32[] memory _keyTuple = new bytes32[](1); @@ -1049,7 +1445,15 @@ library Dynamics1 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + StoreSwitch.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /* Delete all data for given keys */ + function _deleteRecord(bytes32 key) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); } /* Delete all data for given keys (using the specified store) */ @@ -1057,7 +1461,7 @@ library Dynamics1 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + _store.deleteRecord(_tableId, _keyTuple, _fieldLayout); } } diff --git a/packages/cli/contracts/src/codegen/tables/Dynamics2.sol b/packages/cli/contracts/src/codegen/tables/Dynamics2.sol index 3bcbe30cd3..77371f92a7 100644 --- a/packages/cli/contracts/src/codegen/tables/Dynamics2.sol +++ b/packages/cli/contracts/src/codegen/tables/Dynamics2.sol @@ -21,6 +21,10 @@ import { PackedCounter, PackedCounterLib } from "@latticexyz/store/src/PackedCou bytes32 constant _tableId = bytes32(abi.encodePacked(bytes16(""), bytes16("Dynamics2"))); bytes32 constant Dynamics2TableId = _tableId; +FieldLayout constant _fieldLayout = FieldLayout.wrap( + 0x0000000300000000000000000000000000000000000000000000000000000000 +); + struct Dynamics2Data { uint64[] u64; string str; @@ -30,9 +34,7 @@ struct Dynamics2Data { library Dynamics2 { /** Get the table values' field layout */ function getFieldLayout() internal pure returns (FieldLayout) { - uint256[] memory _fieldLayout = new uint256[](0); - - return FieldLayoutLib.encode(_fieldLayout, 3); + return _fieldLayout; } /** Get the table's key schema */ @@ -69,19 +71,17 @@ library Dynamics2 { /** Register the table with its config */ function register() internal { - StoreSwitch.registerTable( - _tableId, - getFieldLayout(), - getKeySchema(), - getValueSchema(), - getKeyNames(), - getFieldNames() - ); + StoreSwitch.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + } + + /** Register the table with its config */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Register the table with its config (using the specified store) */ function register(IStore _store) internal { - _store.registerTable(_tableId, getFieldLayout(), getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + _store.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Get u64 */ @@ -89,7 +89,16 @@ library Dynamics2 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 0, getFieldLayout()); + bytes memory _blob = StoreSwitch.getDynamicField(_tableId, _keyTuple, 0); + return (SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_uint64()); + } + + /** Get u64 */ + function _getU64(bytes32 key) internal view returns (uint64[] memory u64) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + bytes memory _blob = StoreCore.getDynamicField(_tableId, _keyTuple, 0); return (SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_uint64()); } @@ -98,7 +107,7 @@ library Dynamics2 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = _store.getField(_tableId, _keyTuple, 0, getFieldLayout()); + bytes memory _blob = _store.getDynamicField(_tableId, _keyTuple, 0); return (SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_uint64()); } @@ -107,7 +116,15 @@ library Dynamics2 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.setField(_tableId, _keyTuple, 0, EncodeArray.encode((u64)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 0, EncodeArray.encode((u64)), _fieldLayout); + } + + /** Set u64 */ + function _setU64(bytes32 key, uint64[] memory u64) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.setField(_tableId, _keyTuple, 0, EncodeArray.encode((u64)), _fieldLayout); } /** Set u64 (using the specified store) */ @@ -115,7 +132,7 @@ library Dynamics2 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.setField(_tableId, _keyTuple, 0, EncodeArray.encode((u64)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 0, EncodeArray.encode((u64)), _fieldLayout); } /** Get the length of u64 */ @@ -123,7 +140,18 @@ library Dynamics2 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 0, getFieldLayout()); + uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 0, _fieldLayout); + unchecked { + return _byteLength / 8; + } + } + + /** Get the length of u64 */ + function _lengthU64(bytes32 key) internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + uint256 _byteLength = StoreCore.getFieldLength(_tableId, _keyTuple, 0, _fieldLayout); unchecked { return _byteLength / 8; } @@ -134,7 +162,7 @@ library Dynamics2 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 0, getFieldLayout()); + uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 0, _fieldLayout); unchecked { return _byteLength / 8; } @@ -153,11 +181,25 @@ library Dynamics2 { _tableId, _keyTuple, 0, - getFieldLayout(), + _fieldLayout, _index * 8, (_index + 1) * 8 ); - return (uint64(Bytes.slice8(_blob, 0))); + return (uint64(bytes8(_blob))); + } + } + + /** + * Get an item of u64 + * (unchecked, returns invalid data if index overflows) + */ + function _getItemU64(bytes32 key, uint256 _index) internal view returns (uint64) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + unchecked { + bytes memory _blob = StoreCore.getFieldSlice(_tableId, _keyTuple, 0, _fieldLayout, _index * 8, (_index + 1) * 8); + return (uint64(bytes8(_blob))); } } @@ -170,8 +212,8 @@ library Dynamics2 { _keyTuple[0] = key; unchecked { - bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 0, getFieldLayout(), _index * 8, (_index + 1) * 8); - return (uint64(Bytes.slice8(_blob, 0))); + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 0, _fieldLayout, _index * 8, (_index + 1) * 8); + return (uint64(bytes8(_blob))); } } @@ -180,7 +222,15 @@ library Dynamics2 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), getFieldLayout()); + StoreSwitch.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), _fieldLayout); + } + + /** Push an element to u64 */ + function _pushU64(bytes32 key, uint64 _element) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), _fieldLayout); } /** Push an element to u64 (using the specified store) */ @@ -188,7 +238,7 @@ library Dynamics2 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), getFieldLayout()); + _store.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), _fieldLayout); } /** Pop an element from u64 */ @@ -196,7 +246,15 @@ library Dynamics2 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.popFromField(_tableId, _keyTuple, 0, 8, getFieldLayout()); + StoreSwitch.popFromField(_tableId, _keyTuple, 0, 8, _fieldLayout); + } + + /** Pop an element from u64 */ + function _popU64(bytes32 key) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.popFromField(_tableId, _keyTuple, 0, 8, _fieldLayout); } /** Pop an element from u64 (using the specified store) */ @@ -204,7 +262,7 @@ library Dynamics2 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.popFromField(_tableId, _keyTuple, 0, 8, getFieldLayout()); + _store.popFromField(_tableId, _keyTuple, 0, 8, _fieldLayout); } /** @@ -216,7 +274,20 @@ library Dynamics2 { _keyTuple[0] = key; unchecked { - StoreSwitch.updateInField(_tableId, _keyTuple, 0, _index * 8, abi.encodePacked((_element)), getFieldLayout()); + StoreSwitch.updateInField(_tableId, _keyTuple, 0, _index * 8, abi.encodePacked((_element)), _fieldLayout); + } + } + + /** + * Update an element of u64 at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ + function _updateU64(bytes32 key, uint256 _index, uint64 _element) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + unchecked { + StoreCore.updateInField(_tableId, _keyTuple, 0, _index * 8, abi.encodePacked((_element)), _fieldLayout); } } @@ -229,7 +300,7 @@ library Dynamics2 { _keyTuple[0] = key; unchecked { - _store.updateInField(_tableId, _keyTuple, 0, _index * 8, abi.encodePacked((_element)), getFieldLayout()); + _store.updateInField(_tableId, _keyTuple, 0, _index * 8, abi.encodePacked((_element)), _fieldLayout); } } @@ -238,7 +309,16 @@ library Dynamics2 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 1, getFieldLayout()); + bytes memory _blob = StoreSwitch.getDynamicField(_tableId, _keyTuple, 1); + return (string(_blob)); + } + + /** Get str */ + function _getStr(bytes32 key) internal view returns (string memory str) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + bytes memory _blob = StoreCore.getDynamicField(_tableId, _keyTuple, 1); return (string(_blob)); } @@ -247,7 +327,7 @@ library Dynamics2 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = _store.getField(_tableId, _keyTuple, 1, getFieldLayout()); + bytes memory _blob = _store.getDynamicField(_tableId, _keyTuple, 1); return (string(_blob)); } @@ -256,7 +336,15 @@ library Dynamics2 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.setField(_tableId, _keyTuple, 1, bytes((str)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 1, bytes((str)), _fieldLayout); + } + + /** Set str */ + function _setStr(bytes32 key, string memory str) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.setField(_tableId, _keyTuple, 1, bytes((str)), _fieldLayout); } /** Set str (using the specified store) */ @@ -264,7 +352,7 @@ library Dynamics2 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.setField(_tableId, _keyTuple, 1, bytes((str)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 1, bytes((str)), _fieldLayout); } /** Get the length of str */ @@ -272,7 +360,18 @@ library Dynamics2 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 1, getFieldLayout()); + uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 1, _fieldLayout); + unchecked { + return _byteLength / 1; + } + } + + /** Get the length of str */ + function _lengthStr(bytes32 key) internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + uint256 _byteLength = StoreCore.getFieldLength(_tableId, _keyTuple, 1, _fieldLayout); unchecked { return _byteLength / 1; } @@ -283,7 +382,7 @@ library Dynamics2 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 1, getFieldLayout()); + uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 1, _fieldLayout); unchecked { return _byteLength / 1; } @@ -302,7 +401,7 @@ library Dynamics2 { _tableId, _keyTuple, 1, - getFieldLayout(), + _fieldLayout, _index * 1, (_index + 1) * 1 ); @@ -310,6 +409,20 @@ library Dynamics2 { } } + /** + * Get an item of str + * (unchecked, returns invalid data if index overflows) + */ + function _getItemStr(bytes32 key, uint256 _index) internal view returns (string memory) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + unchecked { + bytes memory _blob = StoreCore.getFieldSlice(_tableId, _keyTuple, 1, _fieldLayout, _index * 1, (_index + 1) * 1); + return (string(_blob)); + } + } + /** * Get an item of str (using the specified store) * (unchecked, returns invalid data if index overflows) @@ -319,7 +432,7 @@ library Dynamics2 { _keyTuple[0] = key; unchecked { - bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 1, getFieldLayout(), _index * 1, (_index + 1) * 1); + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 1, _fieldLayout, _index * 1, (_index + 1) * 1); return (string(_blob)); } } @@ -329,7 +442,15 @@ library Dynamics2 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.pushToField(_tableId, _keyTuple, 1, bytes((_slice)), getFieldLayout()); + StoreSwitch.pushToField(_tableId, _keyTuple, 1, bytes((_slice)), _fieldLayout); + } + + /** Push a slice to str */ + function _pushStr(bytes32 key, string memory _slice) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.pushToField(_tableId, _keyTuple, 1, bytes((_slice)), _fieldLayout); } /** Push a slice to str (using the specified store) */ @@ -337,7 +458,7 @@ library Dynamics2 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.pushToField(_tableId, _keyTuple, 1, bytes((_slice)), getFieldLayout()); + _store.pushToField(_tableId, _keyTuple, 1, bytes((_slice)), _fieldLayout); } /** Pop a slice from str */ @@ -345,7 +466,15 @@ library Dynamics2 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.popFromField(_tableId, _keyTuple, 1, 1, getFieldLayout()); + StoreSwitch.popFromField(_tableId, _keyTuple, 1, 1, _fieldLayout); + } + + /** Pop a slice from str */ + function _popStr(bytes32 key) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.popFromField(_tableId, _keyTuple, 1, 1, _fieldLayout); } /** Pop a slice from str (using the specified store) */ @@ -353,7 +482,7 @@ library Dynamics2 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.popFromField(_tableId, _keyTuple, 1, 1, getFieldLayout()); + _store.popFromField(_tableId, _keyTuple, 1, 1, _fieldLayout); } /** @@ -365,7 +494,20 @@ library Dynamics2 { _keyTuple[0] = key; unchecked { - StoreSwitch.updateInField(_tableId, _keyTuple, 1, _index * 1, bytes((_slice)), getFieldLayout()); + StoreSwitch.updateInField(_tableId, _keyTuple, 1, _index * 1, bytes((_slice)), _fieldLayout); + } + } + + /** + * Update a slice of str at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ + function _updateStr(bytes32 key, uint256 _index, string memory _slice) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + unchecked { + StoreCore.updateInField(_tableId, _keyTuple, 1, _index * 1, bytes((_slice)), _fieldLayout); } } @@ -378,7 +520,7 @@ library Dynamics2 { _keyTuple[0] = key; unchecked { - _store.updateInField(_tableId, _keyTuple, 1, _index * 1, bytes((_slice)), getFieldLayout()); + _store.updateInField(_tableId, _keyTuple, 1, _index * 1, bytes((_slice)), _fieldLayout); } } @@ -387,7 +529,16 @@ library Dynamics2 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 2, getFieldLayout()); + bytes memory _blob = StoreSwitch.getDynamicField(_tableId, _keyTuple, 2); + return (bytes(_blob)); + } + + /** Get b */ + function _getB(bytes32 key) internal view returns (bytes memory b) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + bytes memory _blob = StoreCore.getDynamicField(_tableId, _keyTuple, 2); return (bytes(_blob)); } @@ -396,7 +547,7 @@ library Dynamics2 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = _store.getField(_tableId, _keyTuple, 2, getFieldLayout()); + bytes memory _blob = _store.getDynamicField(_tableId, _keyTuple, 2); return (bytes(_blob)); } @@ -405,7 +556,15 @@ library Dynamics2 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.setField(_tableId, _keyTuple, 2, bytes((b)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 2, bytes((b)), _fieldLayout); + } + + /** Set b */ + function _setB(bytes32 key, bytes memory b) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.setField(_tableId, _keyTuple, 2, bytes((b)), _fieldLayout); } /** Set b (using the specified store) */ @@ -413,7 +572,7 @@ library Dynamics2 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.setField(_tableId, _keyTuple, 2, bytes((b)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 2, bytes((b)), _fieldLayout); } /** Get the length of b */ @@ -421,7 +580,18 @@ library Dynamics2 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 2, getFieldLayout()); + uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 2, _fieldLayout); + unchecked { + return _byteLength / 1; + } + } + + /** Get the length of b */ + function _lengthB(bytes32 key) internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + uint256 _byteLength = StoreCore.getFieldLength(_tableId, _keyTuple, 2, _fieldLayout); unchecked { return _byteLength / 1; } @@ -432,7 +602,7 @@ library Dynamics2 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 2, getFieldLayout()); + uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 2, _fieldLayout); unchecked { return _byteLength / 1; } @@ -451,7 +621,7 @@ library Dynamics2 { _tableId, _keyTuple, 2, - getFieldLayout(), + _fieldLayout, _index * 1, (_index + 1) * 1 ); @@ -459,6 +629,20 @@ library Dynamics2 { } } + /** + * Get an item of b + * (unchecked, returns invalid data if index overflows) + */ + function _getItemB(bytes32 key, uint256 _index) internal view returns (bytes memory) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + unchecked { + bytes memory _blob = StoreCore.getFieldSlice(_tableId, _keyTuple, 2, _fieldLayout, _index * 1, (_index + 1) * 1); + return (bytes(_blob)); + } + } + /** * Get an item of b (using the specified store) * (unchecked, returns invalid data if index overflows) @@ -468,7 +652,7 @@ library Dynamics2 { _keyTuple[0] = key; unchecked { - bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 2, getFieldLayout(), _index * 1, (_index + 1) * 1); + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 2, _fieldLayout, _index * 1, (_index + 1) * 1); return (bytes(_blob)); } } @@ -478,7 +662,15 @@ library Dynamics2 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.pushToField(_tableId, _keyTuple, 2, bytes((_slice)), getFieldLayout()); + StoreSwitch.pushToField(_tableId, _keyTuple, 2, bytes((_slice)), _fieldLayout); + } + + /** Push a slice to b */ + function _pushB(bytes32 key, bytes memory _slice) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.pushToField(_tableId, _keyTuple, 2, bytes((_slice)), _fieldLayout); } /** Push a slice to b (using the specified store) */ @@ -486,7 +678,7 @@ library Dynamics2 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.pushToField(_tableId, _keyTuple, 2, bytes((_slice)), getFieldLayout()); + _store.pushToField(_tableId, _keyTuple, 2, bytes((_slice)), _fieldLayout); } /** Pop a slice from b */ @@ -494,7 +686,15 @@ library Dynamics2 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.popFromField(_tableId, _keyTuple, 2, 1, getFieldLayout()); + StoreSwitch.popFromField(_tableId, _keyTuple, 2, 1, _fieldLayout); + } + + /** Pop a slice from b */ + function _popB(bytes32 key) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.popFromField(_tableId, _keyTuple, 2, 1, _fieldLayout); } /** Pop a slice from b (using the specified store) */ @@ -502,7 +702,7 @@ library Dynamics2 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.popFromField(_tableId, _keyTuple, 2, 1, getFieldLayout()); + _store.popFromField(_tableId, _keyTuple, 2, 1, _fieldLayout); } /** @@ -514,7 +714,20 @@ library Dynamics2 { _keyTuple[0] = key; unchecked { - StoreSwitch.updateInField(_tableId, _keyTuple, 2, _index * 1, bytes((_slice)), getFieldLayout()); + StoreSwitch.updateInField(_tableId, _keyTuple, 2, _index * 1, bytes((_slice)), _fieldLayout); + } + } + + /** + * Update a slice of b at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ + function _updateB(bytes32 key, uint256 _index, bytes memory _slice) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + unchecked { + StoreCore.updateInField(_tableId, _keyTuple, 2, _index * 1, bytes((_slice)), _fieldLayout); } } @@ -527,7 +740,7 @@ library Dynamics2 { _keyTuple[0] = key; unchecked { - _store.updateInField(_tableId, _keyTuple, 2, _index * 1, bytes((_slice)), getFieldLayout()); + _store.updateInField(_tableId, _keyTuple, 2, _index * 1, bytes((_slice)), _fieldLayout); } } @@ -536,7 +749,16 @@ library Dynamics2 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = StoreSwitch.getRecord(_tableId, _keyTuple, getFieldLayout()); + bytes memory _blob = StoreSwitch.getRecord(_tableId, _keyTuple, _fieldLayout); + return decode(_blob); + } + + /** Get the full data */ + function _get(bytes32 key) internal view returns (Dynamics2Data memory _table) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + bytes memory _blob = StoreCore.getRecord(_tableId, _keyTuple, _fieldLayout); return decode(_blob); } @@ -545,28 +767,44 @@ library Dynamics2 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = _store.getRecord(_tableId, _keyTuple, getFieldLayout()); + bytes memory _blob = _store.getRecord(_tableId, _keyTuple, _fieldLayout); return decode(_blob); } /** Set the full data using individual values */ function set(bytes32 key, uint64[] memory u64, string memory str, bytes memory b) internal { - bytes memory _data = encode(u64, str, b); + bytes memory _staticData; + PackedCounter _encodedLengths = encodeLengths(u64, str, b); + bytes memory _dynamicData = encodeDynamic(u64, str, b); bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.setRecord(_tableId, _keyTuple, _data, getFieldLayout()); + StoreSwitch.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); + } + + /** Set the full data using individual values */ + function _set(bytes32 key, uint64[] memory u64, string memory str, bytes memory b) internal { + bytes memory _staticData; + PackedCounter _encodedLengths = encodeLengths(u64, str, b); + bytes memory _dynamicData = encodeDynamic(u64, str, b); + + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); } /** Set the full data using individual values (using the specified store) */ function set(IStore _store, bytes32 key, uint64[] memory u64, string memory str, bytes memory b) internal { - bytes memory _data = encode(u64, str, b); + bytes memory _staticData; + PackedCounter _encodedLengths = encodeLengths(u64, str, b); + bytes memory _dynamicData = encodeDynamic(u64, str, b); bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.setRecord(_tableId, _keyTuple, _data, getFieldLayout()); + _store.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); } /** Set the full data using the data struct */ @@ -574,6 +812,11 @@ library Dynamics2 { set(key, _table.u64, _table.str, _table.b); } + /** Set the full data using the data struct */ + function _set(bytes32 key, Dynamics2Data memory _table) internal { + set(key, _table.u64, _table.str, _table.b); + } + /** Set the full data using the data struct (using the specified store) */ function set(IStore _store, bytes32 key, Dynamics2Data memory _table) internal { set(_store, key, _table.u64, _table.str, _table.b); @@ -611,15 +854,30 @@ library Dynamics2 { } } - /** Tightly pack full data using this table's field layout */ - function encode(uint64[] memory u64, string memory str, bytes memory b) internal pure returns (bytes memory) { - PackedCounter _encodedLengths; + /** Tightly pack dynamic data using this table's schema */ + function encodeLengths( + uint64[] memory u64, + string memory str, + bytes memory b + ) internal pure returns (PackedCounter _encodedLengths) { // Lengths are effectively checked during copy by 2**40 bytes exceeding gas limits unchecked { _encodedLengths = PackedCounterLib.pack(u64.length * 8, bytes(str).length, bytes(b).length); } + } + + /** Tightly pack dynamic data using this table's schema */ + function encodeDynamic(uint64[] memory u64, string memory str, bytes memory b) internal pure returns (bytes memory) { + return abi.encodePacked(EncodeArray.encode((u64)), bytes((str)), bytes((b))); + } + + /** Tightly pack full data using this table's field layout */ + function encode(uint64[] memory u64, string memory str, bytes memory b) internal pure returns (bytes memory) { + bytes memory _staticData; + PackedCounter _encodedLengths = encodeLengths(u64, str, b); + bytes memory _dynamicData = encodeDynamic(u64, str, b); - return abi.encodePacked(_encodedLengths.unwrap(), EncodeArray.encode((u64)), bytes((str)), bytes((b))); + return abi.encodePacked(_staticData, _encodedLengths, _dynamicData); } /** Encode keys as a bytes32 array using this table's field layout */ @@ -635,7 +893,15 @@ library Dynamics2 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + StoreSwitch.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /* Delete all data for given keys */ + function _deleteRecord(bytes32 key) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); } /* Delete all data for given keys (using the specified store) */ @@ -643,6 +909,6 @@ library Dynamics2 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + _store.deleteRecord(_tableId, _keyTuple, _fieldLayout); } } diff --git a/packages/cli/contracts/src/codegen/tables/Ephemeral.sol b/packages/cli/contracts/src/codegen/tables/Ephemeral.sol index 0f9cec20cc..04e78e2503 100644 --- a/packages/cli/contracts/src/codegen/tables/Ephemeral.sol +++ b/packages/cli/contracts/src/codegen/tables/Ephemeral.sol @@ -21,13 +21,14 @@ import { PackedCounter, PackedCounterLib } from "@latticexyz/store/src/PackedCou bytes32 constant _tableId = bytes32(abi.encodePacked(bytes16(""), bytes16("Ephemeral"))); bytes32 constant EphemeralTableId = _tableId; +FieldLayout constant _fieldLayout = FieldLayout.wrap( + 0x0020010020000000000000000000000000000000000000000000000000000000 +); + library Ephemeral { /** Get the table values' field layout */ function getFieldLayout() internal pure returns (FieldLayout) { - uint256[] memory _fieldLayout = new uint256[](1); - _fieldLayout[0] = 32; - - return FieldLayoutLib.encode(_fieldLayout, 0); + return _fieldLayout; } /** Get the table's key schema */ @@ -60,44 +61,71 @@ library Ephemeral { /** Register the table with its config */ function register() internal { - StoreSwitch.registerTable( - _tableId, - getFieldLayout(), - getKeySchema(), - getValueSchema(), - getKeyNames(), - getFieldNames() - ); + StoreSwitch.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + } + + /** Register the table with its config */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Register the table with its config (using the specified store) */ function register(IStore _store) internal { - _store.registerTable(_tableId, getFieldLayout(), getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + _store.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Emit the ephemeral event using individual values */ function emitEphemeral(bytes32 key, uint256 value) internal { - bytes memory _data = encode(value); + bytes memory _staticData = encodeStatic(value); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.emitEphemeralRecord(_tableId, _keyTuple, _data, getFieldLayout()); + StoreSwitch.emitEphemeralRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); + } + + /** Emit the ephemeral event using individual values */ + function _emitEphemeral(bytes32 key, uint256 value) internal { + bytes memory _staticData = encodeStatic(value); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; + + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.emitEphemeralRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); } /** Emit the ephemeral event using individual values (using the specified store) */ function emitEphemeral(IStore _store, bytes32 key, uint256 value) internal { - bytes memory _data = encode(value); + bytes memory _staticData = encodeStatic(value); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.emitEphemeralRecord(_tableId, _keyTuple, _data, getFieldLayout()); + _store.emitEphemeralRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); + } + + /** Tightly pack static data using this table's schema */ + function encodeStatic(uint256 value) internal pure returns (bytes memory) { + return abi.encodePacked(value); } /** Tightly pack full data using this table's field layout */ function encode(uint256 value) internal pure returns (bytes memory) { - return abi.encodePacked(value); + bytes memory _staticData = encodeStatic(value); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; + + return abi.encodePacked(_staticData, _encodedLengths, _dynamicData); } /** Encode keys as a bytes32 array using this table's field layout */ diff --git a/packages/cli/contracts/src/codegen/tables/Singleton.sol b/packages/cli/contracts/src/codegen/tables/Singleton.sol index c309eedd60..3db7f72067 100644 --- a/packages/cli/contracts/src/codegen/tables/Singleton.sol +++ b/packages/cli/contracts/src/codegen/tables/Singleton.sol @@ -21,13 +21,14 @@ import { PackedCounter, PackedCounterLib } from "@latticexyz/store/src/PackedCou bytes32 constant _tableId = bytes32(abi.encodePacked(bytes16(""), bytes16("Singleton"))); bytes32 constant SingletonTableId = _tableId; +FieldLayout constant _fieldLayout = FieldLayout.wrap( + 0x0020010320000000000000000000000000000000000000000000000000000000 +); + library Singleton { /** Get the table values' field layout */ function getFieldLayout() internal pure returns (FieldLayout) { - uint256[] memory _fieldLayout = new uint256[](1); - _fieldLayout[0] = 32; - - return FieldLayoutLib.encode(_fieldLayout, 3); + return _fieldLayout; } /** Get the table's key schema */ @@ -64,56 +65,77 @@ library Singleton { /** Register the table with its config */ function register() internal { - StoreSwitch.registerTable( - _tableId, - getFieldLayout(), - getKeySchema(), - getValueSchema(), - getKeyNames(), - getFieldNames() - ); + StoreSwitch.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + } + + /** Register the table with its config */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Register the table with its config (using the specified store) */ function register(IStore _store) internal { - _store.registerTable(_tableId, getFieldLayout(), getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + _store.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Get v1 */ function getV1() internal view returns (int256 v1) { bytes32[] memory _keyTuple = new bytes32[](0); - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (int256(uint256(Bytes.slice32(_blob, 0)))); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (int256(uint256(bytes32(_blob)))); + } + + /** Get v1 */ + function _getV1() internal view returns (int256 v1) { + bytes32[] memory _keyTuple = new bytes32[](0); + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (int256(uint256(bytes32(_blob)))); } /** Get v1 (using the specified store) */ function getV1(IStore _store) internal view returns (int256 v1) { bytes32[] memory _keyTuple = new bytes32[](0); - bytes memory _blob = _store.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (int256(uint256(Bytes.slice32(_blob, 0)))); + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (int256(uint256(bytes32(_blob)))); } /** Set v1 */ function setV1(int256 v1) internal { bytes32[] memory _keyTuple = new bytes32[](0); - StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((v1)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((v1)), _fieldLayout); + } + + /** Set v1 */ + function _setV1(int256 v1) internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreCore.setField(_tableId, _keyTuple, 0, abi.encodePacked((v1)), _fieldLayout); } /** Set v1 (using the specified store) */ function setV1(IStore _store, int256 v1) internal { bytes32[] memory _keyTuple = new bytes32[](0); - _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((v1)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((v1)), _fieldLayout); } /** Get v2 */ function getV2() internal view returns (uint32[2] memory v2) { bytes32[] memory _keyTuple = new bytes32[](0); - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 1, getFieldLayout()); + bytes memory _blob = StoreSwitch.getDynamicField(_tableId, _keyTuple, 0); + return toStaticArray_uint32_2(SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_uint32()); + } + + /** Get v2 */ + function _getV2() internal view returns (uint32[2] memory v2) { + bytes32[] memory _keyTuple = new bytes32[](0); + + bytes memory _blob = StoreCore.getDynamicField(_tableId, _keyTuple, 0); return toStaticArray_uint32_2(SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_uint32()); } @@ -121,7 +143,7 @@ library Singleton { function getV2(IStore _store) internal view returns (uint32[2] memory v2) { bytes32[] memory _keyTuple = new bytes32[](0); - bytes memory _blob = _store.getField(_tableId, _keyTuple, 1, getFieldLayout()); + bytes memory _blob = _store.getDynamicField(_tableId, _keyTuple, 0); return toStaticArray_uint32_2(SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_uint32()); } @@ -129,21 +151,38 @@ library Singleton { function setV2(uint32[2] memory v2) internal { bytes32[] memory _keyTuple = new bytes32[](0); - StoreSwitch.setField(_tableId, _keyTuple, 1, EncodeArray.encode(fromStaticArray_uint32_2(v2)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 1, EncodeArray.encode(fromStaticArray_uint32_2(v2)), _fieldLayout); + } + + /** Set v2 */ + function _setV2(uint32[2] memory v2) internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreCore.setField(_tableId, _keyTuple, 1, EncodeArray.encode(fromStaticArray_uint32_2(v2)), _fieldLayout); } /** Set v2 (using the specified store) */ function setV2(IStore _store, uint32[2] memory v2) internal { bytes32[] memory _keyTuple = new bytes32[](0); - _store.setField(_tableId, _keyTuple, 1, EncodeArray.encode(fromStaticArray_uint32_2(v2)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 1, EncodeArray.encode(fromStaticArray_uint32_2(v2)), _fieldLayout); } /** Get the length of v2 */ function lengthV2() internal view returns (uint256) { bytes32[] memory _keyTuple = new bytes32[](0); - uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 1, getFieldLayout()); + uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 1, _fieldLayout); + unchecked { + return _byteLength / 4; + } + } + + /** Get the length of v2 */ + function _lengthV2() internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](0); + + uint256 _byteLength = StoreCore.getFieldLength(_tableId, _keyTuple, 1, _fieldLayout); unchecked { return _byteLength / 4; } @@ -153,7 +192,7 @@ library Singleton { function lengthV2(IStore _store) internal view returns (uint256) { bytes32[] memory _keyTuple = new bytes32[](0); - uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 1, getFieldLayout()); + uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 1, _fieldLayout); unchecked { return _byteLength / 4; } @@ -171,11 +210,24 @@ library Singleton { _tableId, _keyTuple, 1, - getFieldLayout(), + _fieldLayout, _index * 4, (_index + 1) * 4 ); - return (uint32(Bytes.slice4(_blob, 0))); + return (uint32(bytes4(_blob))); + } + } + + /** + * Get an item of v2 + * (unchecked, returns invalid data if index overflows) + */ + function _getItemV2(uint256 _index) internal view returns (uint32) { + bytes32[] memory _keyTuple = new bytes32[](0); + + unchecked { + bytes memory _blob = StoreCore.getFieldSlice(_tableId, _keyTuple, 1, _fieldLayout, _index * 4, (_index + 1) * 4); + return (uint32(bytes4(_blob))); } } @@ -187,8 +239,8 @@ library Singleton { bytes32[] memory _keyTuple = new bytes32[](0); unchecked { - bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 1, getFieldLayout(), _index * 4, (_index + 1) * 4); - return (uint32(Bytes.slice4(_blob, 0))); + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 1, _fieldLayout, _index * 4, (_index + 1) * 4); + return (uint32(bytes4(_blob))); } } @@ -196,28 +248,42 @@ library Singleton { function pushV2(uint32 _element) internal { bytes32[] memory _keyTuple = new bytes32[](0); - StoreSwitch.pushToField(_tableId, _keyTuple, 1, abi.encodePacked((_element)), getFieldLayout()); + StoreSwitch.pushToField(_tableId, _keyTuple, 1, abi.encodePacked((_element)), _fieldLayout); + } + + /** Push an element to v2 */ + function _pushV2(uint32 _element) internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreCore.pushToField(_tableId, _keyTuple, 1, abi.encodePacked((_element)), _fieldLayout); } /** Push an element to v2 (using the specified store) */ function pushV2(IStore _store, uint32 _element) internal { bytes32[] memory _keyTuple = new bytes32[](0); - _store.pushToField(_tableId, _keyTuple, 1, abi.encodePacked((_element)), getFieldLayout()); + _store.pushToField(_tableId, _keyTuple, 1, abi.encodePacked((_element)), _fieldLayout); } /** Pop an element from v2 */ function popV2() internal { bytes32[] memory _keyTuple = new bytes32[](0); - StoreSwitch.popFromField(_tableId, _keyTuple, 1, 4, getFieldLayout()); + StoreSwitch.popFromField(_tableId, _keyTuple, 1, 4, _fieldLayout); + } + + /** Pop an element from v2 */ + function _popV2() internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreCore.popFromField(_tableId, _keyTuple, 1, 4, _fieldLayout); } /** Pop an element from v2 (using the specified store) */ function popV2(IStore _store) internal { bytes32[] memory _keyTuple = new bytes32[](0); - _store.popFromField(_tableId, _keyTuple, 1, 4, getFieldLayout()); + _store.popFromField(_tableId, _keyTuple, 1, 4, _fieldLayout); } /** @@ -228,7 +294,19 @@ library Singleton { bytes32[] memory _keyTuple = new bytes32[](0); unchecked { - StoreSwitch.updateInField(_tableId, _keyTuple, 1, _index * 4, abi.encodePacked((_element)), getFieldLayout()); + StoreSwitch.updateInField(_tableId, _keyTuple, 1, _index * 4, abi.encodePacked((_element)), _fieldLayout); + } + } + + /** + * Update an element of v2 at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ + function _updateV2(uint256 _index, uint32 _element) internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + unchecked { + StoreCore.updateInField(_tableId, _keyTuple, 1, _index * 4, abi.encodePacked((_element)), _fieldLayout); } } @@ -240,7 +318,7 @@ library Singleton { bytes32[] memory _keyTuple = new bytes32[](0); unchecked { - _store.updateInField(_tableId, _keyTuple, 1, _index * 4, abi.encodePacked((_element)), getFieldLayout()); + _store.updateInField(_tableId, _keyTuple, 1, _index * 4, abi.encodePacked((_element)), _fieldLayout); } } @@ -248,7 +326,15 @@ library Singleton { function getV3() internal view returns (uint32[2] memory v3) { bytes32[] memory _keyTuple = new bytes32[](0); - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 2, getFieldLayout()); + bytes memory _blob = StoreSwitch.getDynamicField(_tableId, _keyTuple, 1); + return toStaticArray_uint32_2(SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_uint32()); + } + + /** Get v3 */ + function _getV3() internal view returns (uint32[2] memory v3) { + bytes32[] memory _keyTuple = new bytes32[](0); + + bytes memory _blob = StoreCore.getDynamicField(_tableId, _keyTuple, 1); return toStaticArray_uint32_2(SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_uint32()); } @@ -256,7 +342,7 @@ library Singleton { function getV3(IStore _store) internal view returns (uint32[2] memory v3) { bytes32[] memory _keyTuple = new bytes32[](0); - bytes memory _blob = _store.getField(_tableId, _keyTuple, 2, getFieldLayout()); + bytes memory _blob = _store.getDynamicField(_tableId, _keyTuple, 1); return toStaticArray_uint32_2(SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_uint32()); } @@ -264,21 +350,38 @@ library Singleton { function setV3(uint32[2] memory v3) internal { bytes32[] memory _keyTuple = new bytes32[](0); - StoreSwitch.setField(_tableId, _keyTuple, 2, EncodeArray.encode(fromStaticArray_uint32_2(v3)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 2, EncodeArray.encode(fromStaticArray_uint32_2(v3)), _fieldLayout); + } + + /** Set v3 */ + function _setV3(uint32[2] memory v3) internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreCore.setField(_tableId, _keyTuple, 2, EncodeArray.encode(fromStaticArray_uint32_2(v3)), _fieldLayout); } /** Set v3 (using the specified store) */ function setV3(IStore _store, uint32[2] memory v3) internal { bytes32[] memory _keyTuple = new bytes32[](0); - _store.setField(_tableId, _keyTuple, 2, EncodeArray.encode(fromStaticArray_uint32_2(v3)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 2, EncodeArray.encode(fromStaticArray_uint32_2(v3)), _fieldLayout); } /** Get the length of v3 */ function lengthV3() internal view returns (uint256) { bytes32[] memory _keyTuple = new bytes32[](0); - uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 2, getFieldLayout()); + uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 2, _fieldLayout); + unchecked { + return _byteLength / 4; + } + } + + /** Get the length of v3 */ + function _lengthV3() internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](0); + + uint256 _byteLength = StoreCore.getFieldLength(_tableId, _keyTuple, 2, _fieldLayout); unchecked { return _byteLength / 4; } @@ -288,7 +391,7 @@ library Singleton { function lengthV3(IStore _store) internal view returns (uint256) { bytes32[] memory _keyTuple = new bytes32[](0); - uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 2, getFieldLayout()); + uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 2, _fieldLayout); unchecked { return _byteLength / 4; } @@ -306,11 +409,24 @@ library Singleton { _tableId, _keyTuple, 2, - getFieldLayout(), + _fieldLayout, _index * 4, (_index + 1) * 4 ); - return (uint32(Bytes.slice4(_blob, 0))); + return (uint32(bytes4(_blob))); + } + } + + /** + * Get an item of v3 + * (unchecked, returns invalid data if index overflows) + */ + function _getItemV3(uint256 _index) internal view returns (uint32) { + bytes32[] memory _keyTuple = new bytes32[](0); + + unchecked { + bytes memory _blob = StoreCore.getFieldSlice(_tableId, _keyTuple, 2, _fieldLayout, _index * 4, (_index + 1) * 4); + return (uint32(bytes4(_blob))); } } @@ -322,8 +438,8 @@ library Singleton { bytes32[] memory _keyTuple = new bytes32[](0); unchecked { - bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 2, getFieldLayout(), _index * 4, (_index + 1) * 4); - return (uint32(Bytes.slice4(_blob, 0))); + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 2, _fieldLayout, _index * 4, (_index + 1) * 4); + return (uint32(bytes4(_blob))); } } @@ -331,28 +447,42 @@ library Singleton { function pushV3(uint32 _element) internal { bytes32[] memory _keyTuple = new bytes32[](0); - StoreSwitch.pushToField(_tableId, _keyTuple, 2, abi.encodePacked((_element)), getFieldLayout()); + StoreSwitch.pushToField(_tableId, _keyTuple, 2, abi.encodePacked((_element)), _fieldLayout); + } + + /** Push an element to v3 */ + function _pushV3(uint32 _element) internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreCore.pushToField(_tableId, _keyTuple, 2, abi.encodePacked((_element)), _fieldLayout); } /** Push an element to v3 (using the specified store) */ function pushV3(IStore _store, uint32 _element) internal { bytes32[] memory _keyTuple = new bytes32[](0); - _store.pushToField(_tableId, _keyTuple, 2, abi.encodePacked((_element)), getFieldLayout()); + _store.pushToField(_tableId, _keyTuple, 2, abi.encodePacked((_element)), _fieldLayout); } /** Pop an element from v3 */ function popV3() internal { bytes32[] memory _keyTuple = new bytes32[](0); - StoreSwitch.popFromField(_tableId, _keyTuple, 2, 4, getFieldLayout()); + StoreSwitch.popFromField(_tableId, _keyTuple, 2, 4, _fieldLayout); + } + + /** Pop an element from v3 */ + function _popV3() internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreCore.popFromField(_tableId, _keyTuple, 2, 4, _fieldLayout); } /** Pop an element from v3 (using the specified store) */ function popV3(IStore _store) internal { bytes32[] memory _keyTuple = new bytes32[](0); - _store.popFromField(_tableId, _keyTuple, 2, 4, getFieldLayout()); + _store.popFromField(_tableId, _keyTuple, 2, 4, _fieldLayout); } /** @@ -363,7 +493,19 @@ library Singleton { bytes32[] memory _keyTuple = new bytes32[](0); unchecked { - StoreSwitch.updateInField(_tableId, _keyTuple, 2, _index * 4, abi.encodePacked((_element)), getFieldLayout()); + StoreSwitch.updateInField(_tableId, _keyTuple, 2, _index * 4, abi.encodePacked((_element)), _fieldLayout); + } + } + + /** + * Update an element of v3 at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ + function _updateV3(uint256 _index, uint32 _element) internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + unchecked { + StoreCore.updateInField(_tableId, _keyTuple, 2, _index * 4, abi.encodePacked((_element)), _fieldLayout); } } @@ -375,7 +517,7 @@ library Singleton { bytes32[] memory _keyTuple = new bytes32[](0); unchecked { - _store.updateInField(_tableId, _keyTuple, 2, _index * 4, abi.encodePacked((_element)), getFieldLayout()); + _store.updateInField(_tableId, _keyTuple, 2, _index * 4, abi.encodePacked((_element)), _fieldLayout); } } @@ -383,7 +525,15 @@ library Singleton { function getV4() internal view returns (uint32[1] memory v4) { bytes32[] memory _keyTuple = new bytes32[](0); - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 3, getFieldLayout()); + bytes memory _blob = StoreSwitch.getDynamicField(_tableId, _keyTuple, 2); + return toStaticArray_uint32_1(SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_uint32()); + } + + /** Get v4 */ + function _getV4() internal view returns (uint32[1] memory v4) { + bytes32[] memory _keyTuple = new bytes32[](0); + + bytes memory _blob = StoreCore.getDynamicField(_tableId, _keyTuple, 2); return toStaticArray_uint32_1(SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_uint32()); } @@ -391,7 +541,7 @@ library Singleton { function getV4(IStore _store) internal view returns (uint32[1] memory v4) { bytes32[] memory _keyTuple = new bytes32[](0); - bytes memory _blob = _store.getField(_tableId, _keyTuple, 3, getFieldLayout()); + bytes memory _blob = _store.getDynamicField(_tableId, _keyTuple, 2); return toStaticArray_uint32_1(SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_uint32()); } @@ -399,21 +549,38 @@ library Singleton { function setV4(uint32[1] memory v4) internal { bytes32[] memory _keyTuple = new bytes32[](0); - StoreSwitch.setField(_tableId, _keyTuple, 3, EncodeArray.encode(fromStaticArray_uint32_1(v4)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 3, EncodeArray.encode(fromStaticArray_uint32_1(v4)), _fieldLayout); + } + + /** Set v4 */ + function _setV4(uint32[1] memory v4) internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreCore.setField(_tableId, _keyTuple, 3, EncodeArray.encode(fromStaticArray_uint32_1(v4)), _fieldLayout); } /** Set v4 (using the specified store) */ function setV4(IStore _store, uint32[1] memory v4) internal { bytes32[] memory _keyTuple = new bytes32[](0); - _store.setField(_tableId, _keyTuple, 3, EncodeArray.encode(fromStaticArray_uint32_1(v4)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 3, EncodeArray.encode(fromStaticArray_uint32_1(v4)), _fieldLayout); } /** Get the length of v4 */ function lengthV4() internal view returns (uint256) { bytes32[] memory _keyTuple = new bytes32[](0); - uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 3, getFieldLayout()); + uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 3, _fieldLayout); + unchecked { + return _byteLength / 4; + } + } + + /** Get the length of v4 */ + function _lengthV4() internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](0); + + uint256 _byteLength = StoreCore.getFieldLength(_tableId, _keyTuple, 3, _fieldLayout); unchecked { return _byteLength / 4; } @@ -423,7 +590,7 @@ library Singleton { function lengthV4(IStore _store) internal view returns (uint256) { bytes32[] memory _keyTuple = new bytes32[](0); - uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 3, getFieldLayout()); + uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 3, _fieldLayout); unchecked { return _byteLength / 4; } @@ -441,11 +608,24 @@ library Singleton { _tableId, _keyTuple, 3, - getFieldLayout(), + _fieldLayout, _index * 4, (_index + 1) * 4 ); - return (uint32(Bytes.slice4(_blob, 0))); + return (uint32(bytes4(_blob))); + } + } + + /** + * Get an item of v4 + * (unchecked, returns invalid data if index overflows) + */ + function _getItemV4(uint256 _index) internal view returns (uint32) { + bytes32[] memory _keyTuple = new bytes32[](0); + + unchecked { + bytes memory _blob = StoreCore.getFieldSlice(_tableId, _keyTuple, 3, _fieldLayout, _index * 4, (_index + 1) * 4); + return (uint32(bytes4(_blob))); } } @@ -457,8 +637,8 @@ library Singleton { bytes32[] memory _keyTuple = new bytes32[](0); unchecked { - bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 3, getFieldLayout(), _index * 4, (_index + 1) * 4); - return (uint32(Bytes.slice4(_blob, 0))); + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 3, _fieldLayout, _index * 4, (_index + 1) * 4); + return (uint32(bytes4(_blob))); } } @@ -466,28 +646,42 @@ library Singleton { function pushV4(uint32 _element) internal { bytes32[] memory _keyTuple = new bytes32[](0); - StoreSwitch.pushToField(_tableId, _keyTuple, 3, abi.encodePacked((_element)), getFieldLayout()); + StoreSwitch.pushToField(_tableId, _keyTuple, 3, abi.encodePacked((_element)), _fieldLayout); + } + + /** Push an element to v4 */ + function _pushV4(uint32 _element) internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreCore.pushToField(_tableId, _keyTuple, 3, abi.encodePacked((_element)), _fieldLayout); } /** Push an element to v4 (using the specified store) */ function pushV4(IStore _store, uint32 _element) internal { bytes32[] memory _keyTuple = new bytes32[](0); - _store.pushToField(_tableId, _keyTuple, 3, abi.encodePacked((_element)), getFieldLayout()); + _store.pushToField(_tableId, _keyTuple, 3, abi.encodePacked((_element)), _fieldLayout); } /** Pop an element from v4 */ function popV4() internal { bytes32[] memory _keyTuple = new bytes32[](0); - StoreSwitch.popFromField(_tableId, _keyTuple, 3, 4, getFieldLayout()); + StoreSwitch.popFromField(_tableId, _keyTuple, 3, 4, _fieldLayout); + } + + /** Pop an element from v4 */ + function _popV4() internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreCore.popFromField(_tableId, _keyTuple, 3, 4, _fieldLayout); } /** Pop an element from v4 (using the specified store) */ function popV4(IStore _store) internal { bytes32[] memory _keyTuple = new bytes32[](0); - _store.popFromField(_tableId, _keyTuple, 3, 4, getFieldLayout()); + _store.popFromField(_tableId, _keyTuple, 3, 4, _fieldLayout); } /** @@ -498,7 +692,19 @@ library Singleton { bytes32[] memory _keyTuple = new bytes32[](0); unchecked { - StoreSwitch.updateInField(_tableId, _keyTuple, 3, _index * 4, abi.encodePacked((_element)), getFieldLayout()); + StoreSwitch.updateInField(_tableId, _keyTuple, 3, _index * 4, abi.encodePacked((_element)), _fieldLayout); + } + } + + /** + * Update an element of v4 at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ + function _updateV4(uint256 _index, uint32 _element) internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + unchecked { + StoreCore.updateInField(_tableId, _keyTuple, 3, _index * 4, abi.encodePacked((_element)), _fieldLayout); } } @@ -510,7 +716,7 @@ library Singleton { bytes32[] memory _keyTuple = new bytes32[](0); unchecked { - _store.updateInField(_tableId, _keyTuple, 3, _index * 4, abi.encodePacked((_element)), getFieldLayout()); + _store.updateInField(_tableId, _keyTuple, 3, _index * 4, abi.encodePacked((_element)), _fieldLayout); } } @@ -518,7 +724,15 @@ library Singleton { function get() internal view returns (int256 v1, uint32[2] memory v2, uint32[2] memory v3, uint32[1] memory v4) { bytes32[] memory _keyTuple = new bytes32[](0); - bytes memory _blob = StoreSwitch.getRecord(_tableId, _keyTuple, getFieldLayout()); + bytes memory _blob = StoreSwitch.getRecord(_tableId, _keyTuple, _fieldLayout); + return decode(_blob); + } + + /** 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 _keyTuple = new bytes32[](0); + + bytes memory _blob = StoreCore.getRecord(_tableId, _keyTuple, _fieldLayout); return decode(_blob); } @@ -528,26 +742,44 @@ library Singleton { ) internal view returns (int256 v1, uint32[2] memory v2, uint32[2] memory v3, uint32[1] memory v4) { bytes32[] memory _keyTuple = new bytes32[](0); - bytes memory _blob = _store.getRecord(_tableId, _keyTuple, getFieldLayout()); + bytes memory _blob = _store.getRecord(_tableId, _keyTuple, _fieldLayout); return decode(_blob); } /** Set the full data using individual values */ function set(int256 v1, uint32[2] memory v2, uint32[2] memory v3, uint32[1] memory v4) internal { - bytes memory _data = encode(v1, v2, v3, v4); + bytes memory _staticData = encodeStatic(v1); + + PackedCounter _encodedLengths = encodeLengths(v2, v3, v4); + bytes memory _dynamicData = encodeDynamic(v2, v3, v4); bytes32[] memory _keyTuple = new bytes32[](0); - StoreSwitch.setRecord(_tableId, _keyTuple, _data, getFieldLayout()); + StoreSwitch.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); + } + + /** Set the full data using individual values */ + function _set(int256 v1, uint32[2] memory v2, uint32[2] memory v3, uint32[1] memory v4) internal { + bytes memory _staticData = encodeStatic(v1); + + PackedCounter _encodedLengths = encodeLengths(v2, v3, v4); + bytes memory _dynamicData = encodeDynamic(v2, v3, v4); + + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreCore.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); } /** Set the full data using individual values (using the specified store) */ function set(IStore _store, int256 v1, uint32[2] memory v2, uint32[2] memory v3, uint32[1] memory v4) internal { - bytes memory _data = encode(v1, v2, v3, v4); + bytes memory _staticData = encodeStatic(v1); + + PackedCounter _encodedLengths = encodeLengths(v2, v3, v4); + bytes memory _dynamicData = encodeDynamic(v2, v3, v4); bytes32[] memory _keyTuple = new bytes32[](0); - _store.setRecord(_tableId, _keyTuple, _data, getFieldLayout()); + _store.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); } /** @@ -586,29 +818,52 @@ library Singleton { } } - /** Tightly pack full data using this table's field layout */ - function encode( - int256 v1, + /** Tightly pack static data using this table's schema */ + function encodeStatic(int256 v1) internal pure returns (bytes memory) { + return abi.encodePacked(v1); + } + + /** Tightly pack dynamic data using this table's schema */ + function encodeLengths( uint32[2] memory v2, uint32[2] memory v3, uint32[1] memory v4 - ) internal pure returns (bytes memory) { - PackedCounter _encodedLengths; + ) internal pure returns (PackedCounter _encodedLengths) { // Lengths are effectively checked during copy by 2**40 bytes exceeding gas limits unchecked { _encodedLengths = PackedCounterLib.pack(v2.length * 4, v3.length * 4, v4.length * 4); } + } + /** Tightly pack dynamic data using this table's schema */ + function encodeDynamic( + uint32[2] memory v2, + uint32[2] memory v3, + uint32[1] memory v4 + ) internal pure returns (bytes memory) { return abi.encodePacked( - v1, - _encodedLengths.unwrap(), EncodeArray.encode(fromStaticArray_uint32_2(v2)), EncodeArray.encode(fromStaticArray_uint32_2(v3)), EncodeArray.encode(fromStaticArray_uint32_1(v4)) ); } + /** Tightly pack full data using this table's field layout */ + function encode( + int256 v1, + uint32[2] memory v2, + uint32[2] memory v3, + uint32[1] memory v4 + ) internal pure returns (bytes memory) { + bytes memory _staticData = encodeStatic(v1); + + PackedCounter _encodedLengths = encodeLengths(v2, v3, v4); + bytes memory _dynamicData = encodeDynamic(v2, v3, v4); + + return abi.encodePacked(_staticData, _encodedLengths, _dynamicData); + } + /** Encode keys as a bytes32 array using this table's field layout */ function encodeKeyTuple() internal pure returns (bytes32[] memory) { bytes32[] memory _keyTuple = new bytes32[](0); @@ -620,14 +875,21 @@ library Singleton { function deleteRecord() internal { bytes32[] memory _keyTuple = new bytes32[](0); - StoreSwitch.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + StoreSwitch.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /* Delete all data for given keys */ + function _deleteRecord() internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); } /* Delete all data for given keys (using the specified store) */ function deleteRecord(IStore _store) internal { bytes32[] memory _keyTuple = new bytes32[](0); - _store.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + _store.deleteRecord(_tableId, _keyTuple, _fieldLayout); } } diff --git a/packages/cli/contracts/src/codegen/tables/Statics.sol b/packages/cli/contracts/src/codegen/tables/Statics.sol index 089552188c..0c74e1b419 100644 --- a/packages/cli/contracts/src/codegen/tables/Statics.sol +++ b/packages/cli/contracts/src/codegen/tables/Statics.sol @@ -19,11 +19,15 @@ import { Schema, SchemaLib } from "@latticexyz/store/src/Schema.sol"; import { PackedCounter, PackedCounterLib } from "@latticexyz/store/src/PackedCounter.sol"; // Import user types -import { Enum1, Enum2 } from "./../Types.sol"; +import { Enum2, Enum1 } from "./../Types.sol"; bytes32 constant _tableId = bytes32(abi.encodePacked(bytes16(""), bytes16("Statics"))); bytes32 constant StaticsTableId = _tableId; +FieldLayout constant _fieldLayout = FieldLayout.wrap( + 0x004a060020041014010100000000000000000000000000000000000000000000 +); + struct StaticsData { uint256 v1; int32 v2; @@ -31,114 +35,103 @@ struct StaticsData { address v4; bool v5; Enum1 v6; - Enum2 v7; } library Statics { /** Get the table values' field layout */ function getFieldLayout() internal pure returns (FieldLayout) { - uint256[] memory _fieldLayout = new uint256[](7); - _fieldLayout[0] = 32; - _fieldLayout[1] = 4; - _fieldLayout[2] = 16; - _fieldLayout[3] = 20; - _fieldLayout[4] = 1; - _fieldLayout[5] = 1; - _fieldLayout[6] = 1; - - return FieldLayoutLib.encode(_fieldLayout, 0); + return _fieldLayout; } /** Get the table's key schema */ function getKeySchema() internal pure returns (Schema) { - SchemaType[] memory _keySchema = new SchemaType[](7); + SchemaType[] memory _keySchema = new SchemaType[](6); _keySchema[0] = SchemaType.UINT256; _keySchema[1] = SchemaType.INT32; _keySchema[2] = SchemaType.BYTES16; _keySchema[3] = SchemaType.ADDRESS; _keySchema[4] = SchemaType.BOOL; _keySchema[5] = SchemaType.UINT8; - _keySchema[6] = SchemaType.UINT8; return SchemaLib.encode(_keySchema); } /** Get the table's value schema */ function getValueSchema() internal pure returns (Schema) { - SchemaType[] memory _valueSchema = new SchemaType[](7); + SchemaType[] memory _valueSchema = new SchemaType[](6); _valueSchema[0] = SchemaType.UINT256; _valueSchema[1] = SchemaType.INT32; _valueSchema[2] = SchemaType.BYTES16; _valueSchema[3] = SchemaType.ADDRESS; _valueSchema[4] = SchemaType.BOOL; _valueSchema[5] = SchemaType.UINT8; - _valueSchema[6] = SchemaType.UINT8; return SchemaLib.encode(_valueSchema); } /** Get the table's key names */ function getKeyNames() internal pure returns (string[] memory keyNames) { - keyNames = new string[](7); + keyNames = new string[](6); keyNames[0] = "k1"; keyNames[1] = "k2"; keyNames[2] = "k3"; keyNames[3] = "k4"; keyNames[4] = "k5"; keyNames[5] = "k6"; - keyNames[6] = "k7"; } /** Get the table's field names */ function getFieldNames() internal pure returns (string[] memory fieldNames) { - fieldNames = new string[](7); + fieldNames = new string[](6); fieldNames[0] = "v1"; fieldNames[1] = "v2"; fieldNames[2] = "v3"; fieldNames[3] = "v4"; fieldNames[4] = "v5"; fieldNames[5] = "v6"; - fieldNames[6] = "v7"; } /** Register the table with its config */ function register() internal { - StoreSwitch.registerTable( - _tableId, - getFieldLayout(), - getKeySchema(), - getValueSchema(), - getKeyNames(), - getFieldNames() - ); + StoreSwitch.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + } + + /** Register the table with its config */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Register the table with its config (using the specified store) */ function register(IStore _store) internal { - _store.registerTable(_tableId, getFieldLayout(), getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + _store.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Get v1 */ - function getV1( - uint256 k1, - int32 k2, - bytes16 k3, - address k4, - bool k5, - Enum1 k6, - Enum2 k7 - ) internal view returns (uint256 v1) { - bytes32[] memory _keyTuple = new bytes32[](7); + function getV1(uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, Enum2 k6) internal view returns (uint256 v1) { + bytes32[] memory _keyTuple = new bytes32[](6); _keyTuple[0] = bytes32(uint256(k1)); _keyTuple[1] = bytes32(uint256(int256(k2))); _keyTuple[2] = bytes32(k3); _keyTuple[3] = bytes32(uint256(uint160(k4))); _keyTuple[4] = _boolToBytes32(k5); _keyTuple[5] = bytes32(uint256(uint8(k6))); - _keyTuple[6] = bytes32(uint256(uint8(k7))); - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (uint256(Bytes.slice32(_blob, 0))); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint256(bytes32(_blob))); + } + + /** Get v1 */ + function _getV1(uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, Enum2 k6) internal view returns (uint256 v1) { + bytes32[] memory _keyTuple = new bytes32[](6); + _keyTuple[0] = bytes32(uint256(k1)); + _keyTuple[1] = bytes32(uint256(int256(k2))); + _keyTuple[2] = bytes32(k3); + _keyTuple[3] = bytes32(uint256(uint160(k4))); + _keyTuple[4] = _boolToBytes32(k5); + _keyTuple[5] = bytes32(uint256(uint8(k6))); + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint256(bytes32(_blob))); } /** Get v1 (using the specified store) */ @@ -149,81 +142,85 @@ library Statics { bytes16 k3, address k4, bool k5, - Enum1 k6, - Enum2 k7 + Enum2 k6 ) internal view returns (uint256 v1) { - bytes32[] memory _keyTuple = new bytes32[](7); + bytes32[] memory _keyTuple = new bytes32[](6); _keyTuple[0] = bytes32(uint256(k1)); _keyTuple[1] = bytes32(uint256(int256(k2))); _keyTuple[2] = bytes32(k3); _keyTuple[3] = bytes32(uint256(uint160(k4))); _keyTuple[4] = _boolToBytes32(k5); _keyTuple[5] = bytes32(uint256(uint8(k6))); - _keyTuple[6] = bytes32(uint256(uint8(k7))); - bytes memory _blob = _store.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (uint256(Bytes.slice32(_blob, 0))); + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint256(bytes32(_blob))); } /** Set v1 */ - function setV1(uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, Enum1 k6, Enum2 k7, uint256 v1) internal { - bytes32[] memory _keyTuple = new bytes32[](7); + function setV1(uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, Enum2 k6, uint256 v1) internal { + bytes32[] memory _keyTuple = new bytes32[](6); _keyTuple[0] = bytes32(uint256(k1)); _keyTuple[1] = bytes32(uint256(int256(k2))); _keyTuple[2] = bytes32(k3); _keyTuple[3] = bytes32(uint256(uint160(k4))); _keyTuple[4] = _boolToBytes32(k5); _keyTuple[5] = bytes32(uint256(uint8(k6))); - _keyTuple[6] = bytes32(uint256(uint8(k7))); - StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((v1)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((v1)), _fieldLayout); + } + + /** Set v1 */ + function _setV1(uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, Enum2 k6, uint256 v1) internal { + bytes32[] memory _keyTuple = new bytes32[](6); + _keyTuple[0] = bytes32(uint256(k1)); + _keyTuple[1] = bytes32(uint256(int256(k2))); + _keyTuple[2] = bytes32(k3); + _keyTuple[3] = bytes32(uint256(uint160(k4))); + _keyTuple[4] = _boolToBytes32(k5); + _keyTuple[5] = bytes32(uint256(uint8(k6))); + + StoreCore.setField(_tableId, _keyTuple, 0, abi.encodePacked((v1)), _fieldLayout); } /** Set v1 (using the specified store) */ - function setV1( - IStore _store, - uint256 k1, - int32 k2, - bytes16 k3, - address k4, - bool k5, - Enum1 k6, - Enum2 k7, - uint256 v1 - ) internal { - bytes32[] memory _keyTuple = new bytes32[](7); + function setV1(IStore _store, uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, Enum2 k6, uint256 v1) internal { + bytes32[] memory _keyTuple = new bytes32[](6); _keyTuple[0] = bytes32(uint256(k1)); _keyTuple[1] = bytes32(uint256(int256(k2))); _keyTuple[2] = bytes32(k3); _keyTuple[3] = bytes32(uint256(uint160(k4))); _keyTuple[4] = _boolToBytes32(k5); _keyTuple[5] = bytes32(uint256(uint8(k6))); - _keyTuple[6] = bytes32(uint256(uint8(k7))); - _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((v1)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((v1)), _fieldLayout); } /** Get v2 */ - function getV2( - uint256 k1, - int32 k2, - bytes16 k3, - address k4, - bool k5, - Enum1 k6, - Enum2 k7 - ) internal view returns (int32 v2) { - bytes32[] memory _keyTuple = new bytes32[](7); + function getV2(uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, Enum2 k6) internal view returns (int32 v2) { + bytes32[] memory _keyTuple = new bytes32[](6); + _keyTuple[0] = bytes32(uint256(k1)); + _keyTuple[1] = bytes32(uint256(int256(k2))); + _keyTuple[2] = bytes32(k3); + _keyTuple[3] = bytes32(uint256(uint160(k4))); + _keyTuple[4] = _boolToBytes32(k5); + _keyTuple[5] = bytes32(uint256(uint8(k6))); + + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 1, _fieldLayout); + return (int32(uint32(bytes4(_blob)))); + } + + /** Get v2 */ + function _getV2(uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, Enum2 k6) internal view returns (int32 v2) { + bytes32[] memory _keyTuple = new bytes32[](6); _keyTuple[0] = bytes32(uint256(k1)); _keyTuple[1] = bytes32(uint256(int256(k2))); _keyTuple[2] = bytes32(k3); _keyTuple[3] = bytes32(uint256(uint160(k4))); _keyTuple[4] = _boolToBytes32(k5); _keyTuple[5] = bytes32(uint256(uint8(k6))); - _keyTuple[6] = bytes32(uint256(uint8(k7))); - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 1, getFieldLayout()); - return (int32(uint32(Bytes.slice4(_blob, 0)))); + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 1, _fieldLayout); + return (int32(uint32(bytes4(_blob)))); } /** Get v2 (using the specified store) */ @@ -234,81 +231,85 @@ library Statics { bytes16 k3, address k4, bool k5, - Enum1 k6, - Enum2 k7 + Enum2 k6 ) internal view returns (int32 v2) { - bytes32[] memory _keyTuple = new bytes32[](7); + bytes32[] memory _keyTuple = new bytes32[](6); _keyTuple[0] = bytes32(uint256(k1)); _keyTuple[1] = bytes32(uint256(int256(k2))); _keyTuple[2] = bytes32(k3); _keyTuple[3] = bytes32(uint256(uint160(k4))); _keyTuple[4] = _boolToBytes32(k5); _keyTuple[5] = bytes32(uint256(uint8(k6))); - _keyTuple[6] = bytes32(uint256(uint8(k7))); - bytes memory _blob = _store.getField(_tableId, _keyTuple, 1, getFieldLayout()); - return (int32(uint32(Bytes.slice4(_blob, 0)))); + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 1, _fieldLayout); + return (int32(uint32(bytes4(_blob)))); } /** Set v2 */ - function setV2(uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, Enum1 k6, Enum2 k7, int32 v2) internal { - bytes32[] memory _keyTuple = new bytes32[](7); + function setV2(uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, Enum2 k6, int32 v2) internal { + bytes32[] memory _keyTuple = new bytes32[](6); _keyTuple[0] = bytes32(uint256(k1)); _keyTuple[1] = bytes32(uint256(int256(k2))); _keyTuple[2] = bytes32(k3); _keyTuple[3] = bytes32(uint256(uint160(k4))); _keyTuple[4] = _boolToBytes32(k5); _keyTuple[5] = bytes32(uint256(uint8(k6))); - _keyTuple[6] = bytes32(uint256(uint8(k7))); - StoreSwitch.setField(_tableId, _keyTuple, 1, abi.encodePacked((v2)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 1, abi.encodePacked((v2)), _fieldLayout); + } + + /** Set v2 */ + function _setV2(uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, Enum2 k6, int32 v2) internal { + bytes32[] memory _keyTuple = new bytes32[](6); + _keyTuple[0] = bytes32(uint256(k1)); + _keyTuple[1] = bytes32(uint256(int256(k2))); + _keyTuple[2] = bytes32(k3); + _keyTuple[3] = bytes32(uint256(uint160(k4))); + _keyTuple[4] = _boolToBytes32(k5); + _keyTuple[5] = bytes32(uint256(uint8(k6))); + + StoreCore.setField(_tableId, _keyTuple, 1, abi.encodePacked((v2)), _fieldLayout); } /** Set v2 (using the specified store) */ - function setV2( - IStore _store, - uint256 k1, - int32 k2, - bytes16 k3, - address k4, - bool k5, - Enum1 k6, - Enum2 k7, - int32 v2 - ) internal { - bytes32[] memory _keyTuple = new bytes32[](7); + function setV2(IStore _store, uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, Enum2 k6, int32 v2) internal { + bytes32[] memory _keyTuple = new bytes32[](6); _keyTuple[0] = bytes32(uint256(k1)); _keyTuple[1] = bytes32(uint256(int256(k2))); _keyTuple[2] = bytes32(k3); _keyTuple[3] = bytes32(uint256(uint160(k4))); _keyTuple[4] = _boolToBytes32(k5); _keyTuple[5] = bytes32(uint256(uint8(k6))); - _keyTuple[6] = bytes32(uint256(uint8(k7))); - _store.setField(_tableId, _keyTuple, 1, abi.encodePacked((v2)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 1, abi.encodePacked((v2)), _fieldLayout); } /** Get v3 */ - function getV3( - uint256 k1, - int32 k2, - bytes16 k3, - address k4, - bool k5, - Enum1 k6, - Enum2 k7 - ) internal view returns (bytes16 v3) { - bytes32[] memory _keyTuple = new bytes32[](7); + function getV3(uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, Enum2 k6) internal view returns (bytes16 v3) { + bytes32[] memory _keyTuple = new bytes32[](6); _keyTuple[0] = bytes32(uint256(k1)); _keyTuple[1] = bytes32(uint256(int256(k2))); _keyTuple[2] = bytes32(k3); _keyTuple[3] = bytes32(uint256(uint160(k4))); _keyTuple[4] = _boolToBytes32(k5); _keyTuple[5] = bytes32(uint256(uint8(k6))); - _keyTuple[6] = bytes32(uint256(uint8(k7))); - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 2, getFieldLayout()); - return (Bytes.slice16(_blob, 0)); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 2, _fieldLayout); + return (bytes16(_blob)); + } + + /** Get v3 */ + function _getV3(uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, Enum2 k6) internal view returns (bytes16 v3) { + bytes32[] memory _keyTuple = new bytes32[](6); + _keyTuple[0] = bytes32(uint256(k1)); + _keyTuple[1] = bytes32(uint256(int256(k2))); + _keyTuple[2] = bytes32(k3); + _keyTuple[3] = bytes32(uint256(uint160(k4))); + _keyTuple[4] = _boolToBytes32(k5); + _keyTuple[5] = bytes32(uint256(uint8(k6))); + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 2, _fieldLayout); + return (bytes16(_blob)); } /** Get v3 (using the specified store) */ @@ -319,81 +320,85 @@ library Statics { bytes16 k3, address k4, bool k5, - Enum1 k6, - Enum2 k7 + Enum2 k6 ) internal view returns (bytes16 v3) { - bytes32[] memory _keyTuple = new bytes32[](7); + bytes32[] memory _keyTuple = new bytes32[](6); _keyTuple[0] = bytes32(uint256(k1)); _keyTuple[1] = bytes32(uint256(int256(k2))); _keyTuple[2] = bytes32(k3); _keyTuple[3] = bytes32(uint256(uint160(k4))); _keyTuple[4] = _boolToBytes32(k5); _keyTuple[5] = bytes32(uint256(uint8(k6))); - _keyTuple[6] = bytes32(uint256(uint8(k7))); - bytes memory _blob = _store.getField(_tableId, _keyTuple, 2, getFieldLayout()); - return (Bytes.slice16(_blob, 0)); + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 2, _fieldLayout); + return (bytes16(_blob)); } /** Set v3 */ - function setV3(uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, Enum1 k6, Enum2 k7, bytes16 v3) internal { - bytes32[] memory _keyTuple = new bytes32[](7); + function setV3(uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, Enum2 k6, bytes16 v3) internal { + bytes32[] memory _keyTuple = new bytes32[](6); _keyTuple[0] = bytes32(uint256(k1)); _keyTuple[1] = bytes32(uint256(int256(k2))); _keyTuple[2] = bytes32(k3); _keyTuple[3] = bytes32(uint256(uint160(k4))); _keyTuple[4] = _boolToBytes32(k5); _keyTuple[5] = bytes32(uint256(uint8(k6))); - _keyTuple[6] = bytes32(uint256(uint8(k7))); - StoreSwitch.setField(_tableId, _keyTuple, 2, abi.encodePacked((v3)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 2, abi.encodePacked((v3)), _fieldLayout); + } + + /** Set v3 */ + function _setV3(uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, Enum2 k6, bytes16 v3) internal { + bytes32[] memory _keyTuple = new bytes32[](6); + _keyTuple[0] = bytes32(uint256(k1)); + _keyTuple[1] = bytes32(uint256(int256(k2))); + _keyTuple[2] = bytes32(k3); + _keyTuple[3] = bytes32(uint256(uint160(k4))); + _keyTuple[4] = _boolToBytes32(k5); + _keyTuple[5] = bytes32(uint256(uint8(k6))); + + StoreCore.setField(_tableId, _keyTuple, 2, abi.encodePacked((v3)), _fieldLayout); } /** Set v3 (using the specified store) */ - function setV3( - IStore _store, - uint256 k1, - int32 k2, - bytes16 k3, - address k4, - bool k5, - Enum1 k6, - Enum2 k7, - bytes16 v3 - ) internal { - bytes32[] memory _keyTuple = new bytes32[](7); + function setV3(IStore _store, uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, Enum2 k6, bytes16 v3) internal { + bytes32[] memory _keyTuple = new bytes32[](6); _keyTuple[0] = bytes32(uint256(k1)); _keyTuple[1] = bytes32(uint256(int256(k2))); _keyTuple[2] = bytes32(k3); _keyTuple[3] = bytes32(uint256(uint160(k4))); _keyTuple[4] = _boolToBytes32(k5); _keyTuple[5] = bytes32(uint256(uint8(k6))); - _keyTuple[6] = bytes32(uint256(uint8(k7))); - _store.setField(_tableId, _keyTuple, 2, abi.encodePacked((v3)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 2, abi.encodePacked((v3)), _fieldLayout); } /** Get v4 */ - function getV4( - uint256 k1, - int32 k2, - bytes16 k3, - address k4, - bool k5, - Enum1 k6, - Enum2 k7 - ) internal view returns (address v4) { - bytes32[] memory _keyTuple = new bytes32[](7); + function getV4(uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, Enum2 k6) internal view returns (address v4) { + bytes32[] memory _keyTuple = new bytes32[](6); + _keyTuple[0] = bytes32(uint256(k1)); + _keyTuple[1] = bytes32(uint256(int256(k2))); + _keyTuple[2] = bytes32(k3); + _keyTuple[3] = bytes32(uint256(uint160(k4))); + _keyTuple[4] = _boolToBytes32(k5); + _keyTuple[5] = bytes32(uint256(uint8(k6))); + + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 3, _fieldLayout); + return (address(bytes20(_blob))); + } + + /** Get v4 */ + function _getV4(uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, Enum2 k6) internal view returns (address v4) { + bytes32[] memory _keyTuple = new bytes32[](6); _keyTuple[0] = bytes32(uint256(k1)); _keyTuple[1] = bytes32(uint256(int256(k2))); _keyTuple[2] = bytes32(k3); _keyTuple[3] = bytes32(uint256(uint160(k4))); _keyTuple[4] = _boolToBytes32(k5); _keyTuple[5] = bytes32(uint256(uint8(k6))); - _keyTuple[6] = bytes32(uint256(uint8(k7))); - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 3, getFieldLayout()); - return (address(Bytes.slice20(_blob, 0))); + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 3, _fieldLayout); + return (address(bytes20(_blob))); } /** Get v4 (using the specified store) */ @@ -404,81 +409,85 @@ library Statics { bytes16 k3, address k4, bool k5, - Enum1 k6, - Enum2 k7 + Enum2 k6 ) internal view returns (address v4) { - bytes32[] memory _keyTuple = new bytes32[](7); + bytes32[] memory _keyTuple = new bytes32[](6); _keyTuple[0] = bytes32(uint256(k1)); _keyTuple[1] = bytes32(uint256(int256(k2))); _keyTuple[2] = bytes32(k3); _keyTuple[3] = bytes32(uint256(uint160(k4))); _keyTuple[4] = _boolToBytes32(k5); _keyTuple[5] = bytes32(uint256(uint8(k6))); - _keyTuple[6] = bytes32(uint256(uint8(k7))); - bytes memory _blob = _store.getField(_tableId, _keyTuple, 3, getFieldLayout()); - return (address(Bytes.slice20(_blob, 0))); + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 3, _fieldLayout); + return (address(bytes20(_blob))); } /** Set v4 */ - function setV4(uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, Enum1 k6, Enum2 k7, address v4) internal { - bytes32[] memory _keyTuple = new bytes32[](7); + function setV4(uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, Enum2 k6, address v4) internal { + bytes32[] memory _keyTuple = new bytes32[](6); _keyTuple[0] = bytes32(uint256(k1)); _keyTuple[1] = bytes32(uint256(int256(k2))); _keyTuple[2] = bytes32(k3); _keyTuple[3] = bytes32(uint256(uint160(k4))); _keyTuple[4] = _boolToBytes32(k5); _keyTuple[5] = bytes32(uint256(uint8(k6))); - _keyTuple[6] = bytes32(uint256(uint8(k7))); - StoreSwitch.setField(_tableId, _keyTuple, 3, abi.encodePacked((v4)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 3, abi.encodePacked((v4)), _fieldLayout); + } + + /** Set v4 */ + function _setV4(uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, Enum2 k6, address v4) internal { + bytes32[] memory _keyTuple = new bytes32[](6); + _keyTuple[0] = bytes32(uint256(k1)); + _keyTuple[1] = bytes32(uint256(int256(k2))); + _keyTuple[2] = bytes32(k3); + _keyTuple[3] = bytes32(uint256(uint160(k4))); + _keyTuple[4] = _boolToBytes32(k5); + _keyTuple[5] = bytes32(uint256(uint8(k6))); + + StoreCore.setField(_tableId, _keyTuple, 3, abi.encodePacked((v4)), _fieldLayout); } /** Set v4 (using the specified store) */ - function setV4( - IStore _store, - uint256 k1, - int32 k2, - bytes16 k3, - address k4, - bool k5, - Enum1 k6, - Enum2 k7, - address v4 - ) internal { - bytes32[] memory _keyTuple = new bytes32[](7); + function setV4(IStore _store, uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, Enum2 k6, address v4) internal { + bytes32[] memory _keyTuple = new bytes32[](6); _keyTuple[0] = bytes32(uint256(k1)); _keyTuple[1] = bytes32(uint256(int256(k2))); _keyTuple[2] = bytes32(k3); _keyTuple[3] = bytes32(uint256(uint160(k4))); _keyTuple[4] = _boolToBytes32(k5); _keyTuple[5] = bytes32(uint256(uint8(k6))); - _keyTuple[6] = bytes32(uint256(uint8(k7))); - _store.setField(_tableId, _keyTuple, 3, abi.encodePacked((v4)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 3, abi.encodePacked((v4)), _fieldLayout); } /** Get v5 */ - function getV5( - uint256 k1, - int32 k2, - bytes16 k3, - address k4, - bool k5, - Enum1 k6, - Enum2 k7 - ) internal view returns (bool v5) { - bytes32[] memory _keyTuple = new bytes32[](7); + function getV5(uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, Enum2 k6) internal view returns (bool v5) { + bytes32[] memory _keyTuple = new bytes32[](6); _keyTuple[0] = bytes32(uint256(k1)); _keyTuple[1] = bytes32(uint256(int256(k2))); _keyTuple[2] = bytes32(k3); _keyTuple[3] = bytes32(uint256(uint160(k4))); _keyTuple[4] = _boolToBytes32(k5); _keyTuple[5] = bytes32(uint256(uint8(k6))); - _keyTuple[6] = bytes32(uint256(uint8(k7))); - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 4, getFieldLayout()); - return (_toBool(uint8(Bytes.slice1(_blob, 0)))); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 4, _fieldLayout); + return (_toBool(uint8(bytes1(_blob)))); + } + + /** Get v5 */ + function _getV5(uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, Enum2 k6) internal view returns (bool v5) { + bytes32[] memory _keyTuple = new bytes32[](6); + _keyTuple[0] = bytes32(uint256(k1)); + _keyTuple[1] = bytes32(uint256(int256(k2))); + _keyTuple[2] = bytes32(k3); + _keyTuple[3] = bytes32(uint256(uint160(k4))); + _keyTuple[4] = _boolToBytes32(k5); + _keyTuple[5] = bytes32(uint256(uint8(k6))); + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 4, _fieldLayout); + return (_toBool(uint8(bytes1(_blob)))); } /** Get v5 (using the specified store) */ @@ -489,250 +498,187 @@ library Statics { bytes16 k3, address k4, bool k5, - Enum1 k6, - Enum2 k7 + Enum2 k6 ) internal view returns (bool v5) { - bytes32[] memory _keyTuple = new bytes32[](7); + bytes32[] memory _keyTuple = new bytes32[](6); _keyTuple[0] = bytes32(uint256(k1)); _keyTuple[1] = bytes32(uint256(int256(k2))); _keyTuple[2] = bytes32(k3); _keyTuple[3] = bytes32(uint256(uint160(k4))); _keyTuple[4] = _boolToBytes32(k5); _keyTuple[5] = bytes32(uint256(uint8(k6))); - _keyTuple[6] = bytes32(uint256(uint8(k7))); - bytes memory _blob = _store.getField(_tableId, _keyTuple, 4, getFieldLayout()); - return (_toBool(uint8(Bytes.slice1(_blob, 0)))); + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 4, _fieldLayout); + return (_toBool(uint8(bytes1(_blob)))); } /** Set v5 */ - function setV5(uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, Enum1 k6, Enum2 k7, bool v5) internal { - bytes32[] memory _keyTuple = new bytes32[](7); + function setV5(uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, Enum2 k6, bool v5) internal { + bytes32[] memory _keyTuple = new bytes32[](6); _keyTuple[0] = bytes32(uint256(k1)); _keyTuple[1] = bytes32(uint256(int256(k2))); _keyTuple[2] = bytes32(k3); _keyTuple[3] = bytes32(uint256(uint160(k4))); _keyTuple[4] = _boolToBytes32(k5); _keyTuple[5] = bytes32(uint256(uint8(k6))); - _keyTuple[6] = bytes32(uint256(uint8(k7))); - StoreSwitch.setField(_tableId, _keyTuple, 4, abi.encodePacked((v5)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 4, abi.encodePacked((v5)), _fieldLayout); } - /** Set v5 (using the specified store) */ - function setV5( - IStore _store, - uint256 k1, - int32 k2, - bytes16 k3, - address k4, - bool k5, - Enum1 k6, - Enum2 k7, - bool v5 - ) internal { - bytes32[] memory _keyTuple = new bytes32[](7); + /** Set v5 */ + function _setV5(uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, Enum2 k6, bool v5) internal { + bytes32[] memory _keyTuple = new bytes32[](6); _keyTuple[0] = bytes32(uint256(k1)); _keyTuple[1] = bytes32(uint256(int256(k2))); _keyTuple[2] = bytes32(k3); _keyTuple[3] = bytes32(uint256(uint160(k4))); _keyTuple[4] = _boolToBytes32(k5); _keyTuple[5] = bytes32(uint256(uint8(k6))); - _keyTuple[6] = bytes32(uint256(uint8(k7))); - _store.setField(_tableId, _keyTuple, 4, abi.encodePacked((v5)), getFieldLayout()); + StoreCore.setField(_tableId, _keyTuple, 4, abi.encodePacked((v5)), _fieldLayout); } - /** Get v6 */ - function getV6( - uint256 k1, - int32 k2, - bytes16 k3, - address k4, - bool k5, - Enum1 k6, - Enum2 k7 - ) internal view returns (Enum1 v6) { - bytes32[] memory _keyTuple = new bytes32[](7); + /** Set v5 (using the specified store) */ + function setV5(IStore _store, uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, Enum2 k6, bool v5) internal { + bytes32[] memory _keyTuple = new bytes32[](6); _keyTuple[0] = bytes32(uint256(k1)); _keyTuple[1] = bytes32(uint256(int256(k2))); _keyTuple[2] = bytes32(k3); _keyTuple[3] = bytes32(uint256(uint160(k4))); _keyTuple[4] = _boolToBytes32(k5); _keyTuple[5] = bytes32(uint256(uint8(k6))); - _keyTuple[6] = bytes32(uint256(uint8(k7))); - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 5, getFieldLayout()); - return Enum1(uint8(Bytes.slice1(_blob, 0))); + _store.setField(_tableId, _keyTuple, 4, abi.encodePacked((v5)), _fieldLayout); } - /** Get v6 (using the specified store) */ - function getV6( - IStore _store, - uint256 k1, - int32 k2, - bytes16 k3, - address k4, - bool k5, - Enum1 k6, - Enum2 k7 - ) internal view returns (Enum1 v6) { - bytes32[] memory _keyTuple = new bytes32[](7); + /** Get v6 */ + function getV6(uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, Enum2 k6) internal view returns (Enum1 v6) { + bytes32[] memory _keyTuple = new bytes32[](6); _keyTuple[0] = bytes32(uint256(k1)); _keyTuple[1] = bytes32(uint256(int256(k2))); _keyTuple[2] = bytes32(k3); _keyTuple[3] = bytes32(uint256(uint160(k4))); _keyTuple[4] = _boolToBytes32(k5); _keyTuple[5] = bytes32(uint256(uint8(k6))); - _keyTuple[6] = bytes32(uint256(uint8(k7))); - bytes memory _blob = _store.getField(_tableId, _keyTuple, 5, getFieldLayout()); - return Enum1(uint8(Bytes.slice1(_blob, 0))); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 5, _fieldLayout); + return Enum1(uint8(bytes1(_blob))); } - /** Set v6 */ - function setV6(uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, Enum1 k6, Enum2 k7, Enum1 v6) internal { - bytes32[] memory _keyTuple = new bytes32[](7); + /** Get v6 */ + function _getV6(uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, Enum2 k6) internal view returns (Enum1 v6) { + bytes32[] memory _keyTuple = new bytes32[](6); _keyTuple[0] = bytes32(uint256(k1)); _keyTuple[1] = bytes32(uint256(int256(k2))); _keyTuple[2] = bytes32(k3); _keyTuple[3] = bytes32(uint256(uint160(k4))); _keyTuple[4] = _boolToBytes32(k5); _keyTuple[5] = bytes32(uint256(uint8(k6))); - _keyTuple[6] = bytes32(uint256(uint8(k7))); - StoreSwitch.setField(_tableId, _keyTuple, 5, abi.encodePacked(uint8(v6)), getFieldLayout()); + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 5, _fieldLayout); + return Enum1(uint8(bytes1(_blob))); } - /** Set v6 (using the specified store) */ - function setV6( + /** Get v6 (using the specified store) */ + function getV6( IStore _store, uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, - Enum1 k6, - Enum2 k7, - Enum1 v6 - ) internal { - bytes32[] memory _keyTuple = new bytes32[](7); + Enum2 k6 + ) internal view returns (Enum1 v6) { + bytes32[] memory _keyTuple = new bytes32[](6); _keyTuple[0] = bytes32(uint256(k1)); _keyTuple[1] = bytes32(uint256(int256(k2))); _keyTuple[2] = bytes32(k3); _keyTuple[3] = bytes32(uint256(uint160(k4))); _keyTuple[4] = _boolToBytes32(k5); _keyTuple[5] = bytes32(uint256(uint8(k6))); - _keyTuple[6] = bytes32(uint256(uint8(k7))); - _store.setField(_tableId, _keyTuple, 5, abi.encodePacked(uint8(v6)), getFieldLayout()); + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 5, _fieldLayout); + return Enum1(uint8(bytes1(_blob))); } - /** Get v7 */ - function getV7( - uint256 k1, - int32 k2, - bytes16 k3, - address k4, - bool k5, - Enum1 k6, - Enum2 k7 - ) internal view returns (Enum2 v7) { - bytes32[] memory _keyTuple = new bytes32[](7); + /** Set v6 */ + function setV6(uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, Enum2 k6, Enum1 v6) internal { + bytes32[] memory _keyTuple = new bytes32[](6); _keyTuple[0] = bytes32(uint256(k1)); _keyTuple[1] = bytes32(uint256(int256(k2))); _keyTuple[2] = bytes32(k3); _keyTuple[3] = bytes32(uint256(uint160(k4))); _keyTuple[4] = _boolToBytes32(k5); _keyTuple[5] = bytes32(uint256(uint8(k6))); - _keyTuple[6] = bytes32(uint256(uint8(k7))); - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 6, getFieldLayout()); - return Enum2(uint8(Bytes.slice1(_blob, 0))); + StoreSwitch.setField(_tableId, _keyTuple, 5, abi.encodePacked(uint8(v6)), _fieldLayout); } - /** Get v7 (using the specified store) */ - function getV7( - IStore _store, - uint256 k1, - int32 k2, - bytes16 k3, - address k4, - bool k5, - Enum1 k6, - Enum2 k7 - ) internal view returns (Enum2 v7) { - bytes32[] memory _keyTuple = new bytes32[](7); + /** Set v6 */ + function _setV6(uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, Enum2 k6, Enum1 v6) internal { + bytes32[] memory _keyTuple = new bytes32[](6); _keyTuple[0] = bytes32(uint256(k1)); _keyTuple[1] = bytes32(uint256(int256(k2))); _keyTuple[2] = bytes32(k3); _keyTuple[3] = bytes32(uint256(uint160(k4))); _keyTuple[4] = _boolToBytes32(k5); _keyTuple[5] = bytes32(uint256(uint8(k6))); - _keyTuple[6] = bytes32(uint256(uint8(k7))); - bytes memory _blob = _store.getField(_tableId, _keyTuple, 6, getFieldLayout()); - return Enum2(uint8(Bytes.slice1(_blob, 0))); + StoreCore.setField(_tableId, _keyTuple, 5, abi.encodePacked(uint8(v6)), _fieldLayout); } - /** Set v7 */ - function setV7(uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, Enum1 k6, Enum2 k7, Enum2 v7) internal { - bytes32[] memory _keyTuple = new bytes32[](7); + /** Set v6 (using the specified store) */ + function setV6(IStore _store, uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, Enum2 k6, Enum1 v6) internal { + bytes32[] memory _keyTuple = new bytes32[](6); _keyTuple[0] = bytes32(uint256(k1)); _keyTuple[1] = bytes32(uint256(int256(k2))); _keyTuple[2] = bytes32(k3); _keyTuple[3] = bytes32(uint256(uint160(k4))); _keyTuple[4] = _boolToBytes32(k5); _keyTuple[5] = bytes32(uint256(uint8(k6))); - _keyTuple[6] = bytes32(uint256(uint8(k7))); - StoreSwitch.setField(_tableId, _keyTuple, 6, abi.encodePacked(uint8(v7)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 5, abi.encodePacked(uint8(v6)), _fieldLayout); } - /** Set v7 (using the specified store) */ - function setV7( - IStore _store, + /** Get the full data */ + function get( uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, - Enum1 k6, - Enum2 k7, - Enum2 v7 - ) internal { - bytes32[] memory _keyTuple = new bytes32[](7); + Enum2 k6 + ) internal view returns (StaticsData memory _table) { + bytes32[] memory _keyTuple = new bytes32[](6); _keyTuple[0] = bytes32(uint256(k1)); _keyTuple[1] = bytes32(uint256(int256(k2))); _keyTuple[2] = bytes32(k3); _keyTuple[3] = bytes32(uint256(uint160(k4))); _keyTuple[4] = _boolToBytes32(k5); _keyTuple[5] = bytes32(uint256(uint8(k6))); - _keyTuple[6] = bytes32(uint256(uint8(k7))); - _store.setField(_tableId, _keyTuple, 6, abi.encodePacked(uint8(v7)), getFieldLayout()); + bytes memory _blob = StoreSwitch.getRecord(_tableId, _keyTuple, _fieldLayout); + return decode(_blob); } /** Get the full data */ - function get( + function _get( uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, - Enum1 k6, - Enum2 k7 + Enum2 k6 ) internal view returns (StaticsData memory _table) { - bytes32[] memory _keyTuple = new bytes32[](7); + bytes32[] memory _keyTuple = new bytes32[](6); _keyTuple[0] = bytes32(uint256(k1)); _keyTuple[1] = bytes32(uint256(int256(k2))); _keyTuple[2] = bytes32(k3); _keyTuple[3] = bytes32(uint256(uint160(k4))); _keyTuple[4] = _boolToBytes32(k5); _keyTuple[5] = bytes32(uint256(uint8(k6))); - _keyTuple[6] = bytes32(uint256(uint8(k7))); - bytes memory _blob = StoreSwitch.getRecord(_tableId, _keyTuple, getFieldLayout()); + bytes memory _blob = StoreCore.getRecord(_tableId, _keyTuple, _fieldLayout); return decode(_blob); } @@ -744,19 +690,17 @@ library Statics { bytes16 k3, address k4, bool k5, - Enum1 k6, - Enum2 k7 + Enum2 k6 ) internal view returns (StaticsData memory _table) { - bytes32[] memory _keyTuple = new bytes32[](7); + bytes32[] memory _keyTuple = new bytes32[](6); _keyTuple[0] = bytes32(uint256(k1)); _keyTuple[1] = bytes32(uint256(int256(k2))); _keyTuple[2] = bytes32(k3); _keyTuple[3] = bytes32(uint256(uint160(k4))); _keyTuple[4] = _boolToBytes32(k5); _keyTuple[5] = bytes32(uint256(uint8(k6))); - _keyTuple[6] = bytes32(uint256(uint8(k7))); - bytes memory _blob = _store.getRecord(_tableId, _keyTuple, getFieldLayout()); + bytes memory _blob = _store.getRecord(_tableId, _keyTuple, _fieldLayout); return decode(_blob); } @@ -767,74 +711,101 @@ library Statics { bytes16 k3, address k4, bool k5, - Enum1 k6, - Enum2 k7, + Enum2 k6, uint256 v1, int32 v2, bytes16 v3, address v4, bool v5, - Enum1 v6, - Enum2 v7 + Enum1 v6 ) internal { - bytes memory _data = encode(v1, v2, v3, v4, v5, v6, v7); + bytes memory _staticData = encodeStatic(v1, v2, v3, v4, v5, v6); - bytes32[] memory _keyTuple = new bytes32[](7); + PackedCounter _encodedLengths; + bytes memory _dynamicData; + + bytes32[] memory _keyTuple = new bytes32[](6); _keyTuple[0] = bytes32(uint256(k1)); _keyTuple[1] = bytes32(uint256(int256(k2))); _keyTuple[2] = bytes32(k3); _keyTuple[3] = bytes32(uint256(uint160(k4))); _keyTuple[4] = _boolToBytes32(k5); _keyTuple[5] = bytes32(uint256(uint8(k6))); - _keyTuple[6] = bytes32(uint256(uint8(k7))); - StoreSwitch.setRecord(_tableId, _keyTuple, _data, getFieldLayout()); + StoreSwitch.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); } - /** Set the full data using individual values (using the specified store) */ - function set( - IStore _store, + /** Set the full data using individual values */ + function _set( uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, - Enum1 k6, - Enum2 k7, + Enum2 k6, uint256 v1, int32 v2, bytes16 v3, address v4, bool v5, - Enum1 v6, - Enum2 v7 + Enum1 v6 ) internal { - bytes memory _data = encode(v1, v2, v3, v4, v5, v6, v7); + bytes memory _staticData = encodeStatic(v1, v2, v3, v4, v5, v6); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; - bytes32[] memory _keyTuple = new bytes32[](7); + bytes32[] memory _keyTuple = new bytes32[](6); _keyTuple[0] = bytes32(uint256(k1)); _keyTuple[1] = bytes32(uint256(int256(k2))); _keyTuple[2] = bytes32(k3); _keyTuple[3] = bytes32(uint256(uint160(k4))); _keyTuple[4] = _boolToBytes32(k5); _keyTuple[5] = bytes32(uint256(uint8(k6))); - _keyTuple[6] = bytes32(uint256(uint8(k7))); - _store.setRecord(_tableId, _keyTuple, _data, getFieldLayout()); + StoreCore.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); } - /** Set the full data using the data struct */ + /** Set the full data using individual values (using the specified store) */ function set( + IStore _store, uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, - Enum1 k6, - Enum2 k7, - StaticsData memory _table + Enum2 k6, + uint256 v1, + int32 v2, + bytes16 v3, + address v4, + bool v5, + Enum1 v6 ) internal { - set(k1, k2, k3, k4, k5, k6, k7, _table.v1, _table.v2, _table.v3, _table.v4, _table.v5, _table.v6, _table.v7); + bytes memory _staticData = encodeStatic(v1, v2, v3, v4, v5, v6); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; + + bytes32[] memory _keyTuple = new bytes32[](6); + _keyTuple[0] = bytes32(uint256(k1)); + _keyTuple[1] = bytes32(uint256(int256(k2))); + _keyTuple[2] = bytes32(k3); + _keyTuple[3] = bytes32(uint256(uint160(k4))); + _keyTuple[4] = _boolToBytes32(k5); + _keyTuple[5] = bytes32(uint256(uint8(k6))); + + _store.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); + } + + /** Set the full data using the data struct */ + function set(uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, Enum2 k6, StaticsData memory _table) internal { + set(k1, k2, k3, k4, k5, k6, _table.v1, _table.v2, _table.v3, _table.v4, _table.v5, _table.v6); + } + + /** Set the full data using the data struct */ + function _set(uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, Enum2 k6, StaticsData memory _table) internal { + set(k1, k2, k3, k4, k5, k6, _table.v1, _table.v2, _table.v3, _table.v4, _table.v5, _table.v6); } /** Set the full data using the data struct (using the specified store) */ @@ -845,27 +816,10 @@ library Statics { bytes16 k3, address k4, bool k5, - Enum1 k6, - Enum2 k7, + Enum2 k6, StaticsData memory _table ) internal { - set( - _store, - k1, - k2, - k3, - k4, - k5, - k6, - k7, - _table.v1, - _table.v2, - _table.v3, - _table.v4, - _table.v5, - _table.v6, - _table.v7 - ); + set(_store, k1, k2, k3, k4, k5, k6, _table.v1, _table.v2, _table.v3, _table.v4, _table.v5, _table.v6); } /** Decode the tightly packed blob using this table's field layout */ @@ -881,8 +835,18 @@ library Statics { _table.v5 = (_toBool(uint8(Bytes.slice1(_blob, 72)))); _table.v6 = Enum1(uint8(Bytes.slice1(_blob, 73))); + } - _table.v7 = Enum2(uint8(Bytes.slice1(_blob, 74))); + /** Tightly pack static data using this table's schema */ + function encodeStatic( + uint256 v1, + int32 v2, + bytes16 v3, + address v4, + bool v5, + Enum1 v6 + ) internal pure returns (bytes memory) { + return abi.encodePacked(v1, v2, v3, v4, v5, v6); } /** Tightly pack full data using this table's field layout */ @@ -892,10 +856,14 @@ library Statics { bytes16 v3, address v4, bool v5, - Enum1 v6, - Enum2 v7 + Enum1 v6 ) internal pure returns (bytes memory) { - return abi.encodePacked(v1, v2, v3, v4, v5, v6, v7); + bytes memory _staticData = encodeStatic(v1, v2, v3, v4, v5, v6); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; + + return abi.encodePacked(_staticData, _encodedLengths, _dynamicData); } /** Encode keys as a bytes32 array using this table's field layout */ @@ -905,56 +873,56 @@ library Statics { bytes16 k3, address k4, bool k5, - Enum1 k6, - Enum2 k7 + Enum2 k6 ) internal pure returns (bytes32[] memory) { - bytes32[] memory _keyTuple = new bytes32[](7); + bytes32[] memory _keyTuple = new bytes32[](6); _keyTuple[0] = bytes32(uint256(k1)); _keyTuple[1] = bytes32(uint256(int256(k2))); _keyTuple[2] = bytes32(k3); _keyTuple[3] = bytes32(uint256(uint160(k4))); _keyTuple[4] = _boolToBytes32(k5); _keyTuple[5] = bytes32(uint256(uint8(k6))); - _keyTuple[6] = bytes32(uint256(uint8(k7))); return _keyTuple; } /* Delete all data for given keys */ - function deleteRecord(uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, Enum1 k6, Enum2 k7) internal { - bytes32[] memory _keyTuple = new bytes32[](7); + function deleteRecord(uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, Enum2 k6) internal { + bytes32[] memory _keyTuple = new bytes32[](6); _keyTuple[0] = bytes32(uint256(k1)); _keyTuple[1] = bytes32(uint256(int256(k2))); _keyTuple[2] = bytes32(k3); _keyTuple[3] = bytes32(uint256(uint160(k4))); _keyTuple[4] = _boolToBytes32(k5); _keyTuple[5] = bytes32(uint256(uint8(k6))); - _keyTuple[6] = bytes32(uint256(uint8(k7))); - StoreSwitch.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + StoreSwitch.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /* Delete all data for given keys */ + function _deleteRecord(uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, Enum2 k6) internal { + bytes32[] memory _keyTuple = new bytes32[](6); + _keyTuple[0] = bytes32(uint256(k1)); + _keyTuple[1] = bytes32(uint256(int256(k2))); + _keyTuple[2] = bytes32(k3); + _keyTuple[3] = bytes32(uint256(uint160(k4))); + _keyTuple[4] = _boolToBytes32(k5); + _keyTuple[5] = bytes32(uint256(uint8(k6))); + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); } /* Delete all data for given keys (using the specified store) */ - function deleteRecord( - IStore _store, - uint256 k1, - int32 k2, - bytes16 k3, - address k4, - bool k5, - Enum1 k6, - Enum2 k7 - ) internal { - bytes32[] memory _keyTuple = new bytes32[](7); + function deleteRecord(IStore _store, uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, Enum2 k6) internal { + bytes32[] memory _keyTuple = new bytes32[](6); _keyTuple[0] = bytes32(uint256(k1)); _keyTuple[1] = bytes32(uint256(int256(k2))); _keyTuple[2] = bytes32(k3); _keyTuple[3] = bytes32(uint256(uint160(k4))); _keyTuple[4] = _boolToBytes32(k5); _keyTuple[5] = bytes32(uint256(uint8(k6))); - _keyTuple[6] = bytes32(uint256(uint8(k7))); - _store.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + _store.deleteRecord(_tableId, _keyTuple, _fieldLayout); } } diff --git a/packages/cli/contracts/test/Tablegen.t.sol b/packages/cli/contracts/test/Tablegen.t.sol index 29f6ec8b29..af41a9f635 100644 --- a/packages/cli/contracts/test/Tablegen.t.sol +++ b/packages/cli/contracts/test/Tablegen.t.sol @@ -17,15 +17,14 @@ contract TablegenTest is Test, StoreMock { bytes16 k3 = hex"02"; address k4 = address(123); bool k5 = true; - Enum1 k6 = Enum1.E3; - Enum2 k7 = Enum2.E1; + Enum2 k6 = Enum2.E1; - Statics.setV1(k1, k2, k3, k4, k5, k6, k7, 4); - assertEq(Statics.getV1(k1, k2, k3, k4, k5, k6, k7), 4); + Statics.setV1(k1, k2, k3, k4, k5, k6, 4); + assertEq(Statics.getV1(k1, k2, k3, k4, k5, k6), 4); - StaticsData memory data = StaticsData(4, -5, hex"06", address(456), false, Enum1.E2, Enum2.E1); - Statics.set(k1, k2, k3, k4, k5, k6, k7, data); - assertEq(abi.encode(Statics.get(k1, k2, k3, k4, k5, k6, k7)), abi.encode(data)); + StaticsData memory data = StaticsData(4, -5, hex"06", address(456), false, Enum1.E2); + Statics.set(k1, k2, k3, k4, k5, k6, data); + assertEq(abi.encode(Statics.get(k1, k2, k3, k4, k5, k6)), abi.encode(data)); } function testDynamicsSetAndGet() public { diff --git a/packages/cli/scripts/generate-test-tables.ts b/packages/cli/scripts/generate-test-tables.ts index d7ca0db655..74ecec0817 100644 --- a/packages/cli/scripts/generate-test-tables.ts +++ b/packages/cli/scripts/generate-test-tables.ts @@ -18,8 +18,7 @@ try { k3: "bytes16", k4: "address", k5: "bool", - k6: "Enum1", - k7: "Enum2", + k6: "Enum2", }, valueSchema: { v1: "uint256", @@ -28,7 +27,6 @@ try { v4: "address", v5: "bool", v6: "Enum1", - v7: "Enum2", }, }, Dynamics1: { diff --git a/packages/common/src/codegen/render-solidity/common.ts b/packages/common/src/codegen/render-solidity/common.ts index a853ff8513..4f20e8d965 100644 --- a/packages/common/src/codegen/render-solidity/common.ts +++ b/packages/common/src/codegen/render-solidity/common.ts @@ -131,14 +131,16 @@ export function renderWithStore( _typedStore: string | undefined, _store: string, _commentSuffix: string, - _untypedStore: string | undefined + _untypedStore: string | undefined, + _methodPrefix: string ) => string ): string { let result = ""; - result += callback(undefined, "StoreSwitch", "", undefined); + result += callback(undefined, "StoreSwitch", "", undefined, ""); + result += callback(undefined, "StoreCore", "", undefined, "_"); if (storeArgument) { - result += "\n" + callback("IStore _store", "_store", " (using the specified store)", "_store"); + result += "\n" + callback("IStore _store", "_store", " (using the specified store)", "_store", ""); } return result; diff --git a/packages/common/src/index.ts b/packages/common/src/index.ts index 95489023b1..d6a59390fa 100644 --- a/packages/common/src/index.ts +++ b/packages/common/src/index.ts @@ -3,5 +3,7 @@ export * from "./createContract"; export * from "./createNonceManager"; export * from "./getBurnerPrivateKey"; export * from "./hexToTableId"; +export * from "./readHex"; +export * from "./spliceHex"; export * from "./tableIdToHex"; export * from "./transportObserver"; diff --git a/packages/protocol-parser/src/readHex.test.ts b/packages/common/src/readHex.test.ts similarity index 100% rename from packages/protocol-parser/src/readHex.test.ts rename to packages/common/src/readHex.test.ts diff --git a/packages/protocol-parser/src/readHex.ts b/packages/common/src/readHex.ts similarity index 100% rename from packages/protocol-parser/src/readHex.ts rename to packages/common/src/readHex.ts diff --git a/packages/common/src/spliceHex.ts b/packages/common/src/spliceHex.ts new file mode 100644 index 0000000000..5ce8c1f6d0 --- /dev/null +++ b/packages/common/src/spliceHex.ts @@ -0,0 +1,6 @@ +import { Hex, concatHex } from "viem"; +import { readHex } from "./readHex"; + +export function spliceHex(data: Hex, start: number, deleteCount = 0, newData: Hex = "0x"): Hex { + return concatHex([readHex(data, 0, start), newData, readHex(data, start + deleteCount)]); +} diff --git a/packages/common/src/type-utils/common.ts b/packages/common/src/type-utils/common.ts index 2e7d66225c..e75f5054dc 100644 --- a/packages/common/src/type-utils/common.ts +++ b/packages/common/src/type-utils/common.ts @@ -19,3 +19,5 @@ export type OrDefaults = { }; export type UnionOmit = T extends any ? Omit : never; +export type UnionKeys = T extends any ? keyof T : never; +export type UnionPick> = T extends any ? Pick> : never; diff --git a/packages/dev-tools/README.md b/packages/dev-tools/README.md index cbab052e80..2b3da880e7 100644 --- a/packages/dev-tools/README.md +++ b/packages/dev-tools/README.md @@ -21,7 +21,7 @@ if (import.meta.env.DEV) { publicClient, walletClient, latestBlock$, - blockStorageOperations$, + storedBlockLogs$, worldAddress, worldAbi, write$, diff --git a/packages/dev-tools/src/DevToolsContext.tsx b/packages/dev-tools/src/DevToolsContext.tsx index 8a9d359e1f..2bc811dec5 100644 --- a/packages/dev-tools/src/DevToolsContext.tsx +++ b/packages/dev-tools/src/DevToolsContext.tsx @@ -1,12 +1,11 @@ import { createContext, ReactNode, useContext, useEffect, useState } from "react"; import { DevToolsOptions } from "./common"; import { ContractWrite } from "@latticexyz/common"; -import { StorageOperation } from "@latticexyz/store-sync"; -import { StoreConfig } from "@latticexyz/store"; +import { StorageAdapterLog } from "@latticexyz/store-sync"; type DevToolsContextValue = DevToolsOptions & { writes: ContractWrite[]; - storageOperations: StorageOperation[]; + storedLogs: StorageAdapterLog[]; }; const DevToolsContext = createContext(null); @@ -28,20 +27,20 @@ export const DevToolsProvider = ({ children, value }: Props) => { return () => sub.unsubscribe(); }, [value.write$]); - const [storageOperations, setStorageOperations] = useState[]>([]); + const [storedLogs, setStoredLogs] = useState([]); useEffect(() => { - const sub = value.blockStorageOperations$.subscribe(({ operations }) => { - setStorageOperations((val) => [...val, ...operations]); + const sub = value.storedBlockLogs$.subscribe(({ logs }) => { + setStoredLogs((val) => [...val, ...logs]); }); return () => sub.unsubscribe(); - }, [value.blockStorageOperations$]); + }, [value.storedBlockLogs$]); return ( {children} diff --git a/packages/dev-tools/src/actions/WriteSummary.tsx b/packages/dev-tools/src/actions/WriteSummary.tsx index fb281fa85d..f7f4dca1a5 100644 --- a/packages/dev-tools/src/actions/WriteSummary.tsx +++ b/packages/dev-tools/src/actions/WriteSummary.tsx @@ -132,6 +132,7 @@ export function WriteSummary({ write }: Props) { {events.map(({ eventName, args }, i) => { const table = hexToTableId((args as any).tableId); + // TODO: dedupe this with logs table so we can get both rendering the same return (
diff --git a/packages/dev-tools/src/common.ts b/packages/dev-tools/src/common.ts index 8ecbcac88c..42475fbab3 100644 --- a/packages/dev-tools/src/common.ts +++ b/packages/dev-tools/src/common.ts @@ -1,7 +1,7 @@ import { Observable } from "rxjs"; import { Abi, Block, Chain, PublicClient, Transport, WalletClient } from "viem"; import { StoreConfig } from "@latticexyz/store"; -import { BlockStorageOperations } from "@latticexyz/store-sync"; +import { StorageAdapterBlock } from "@latticexyz/store-sync"; import { ContractWrite } from "@latticexyz/common"; import { World as RecsWorld } from "@latticexyz/recs"; @@ -10,7 +10,7 @@ export type DevToolsOptions = { publicClient: PublicClient; walletClient: WalletClient; latestBlock$: Observable; - blockStorageOperations$: Observable>; + storedBlockLogs$: Observable; worldAddress: string | null; worldAbi: Abi; write$: Observable; diff --git a/packages/dev-tools/src/events/EventIcon.tsx b/packages/dev-tools/src/events/EventIcon.tsx index 8b6aa682e3..8b40c01690 100644 --- a/packages/dev-tools/src/events/EventIcon.tsx +++ b/packages/dev-tools/src/events/EventIcon.tsx @@ -1,22 +1,22 @@ import { assertExhaustive } from "@latticexyz/common/utils"; -import { StoreConfig } from "@latticexyz/store"; -import { StorageOperation } from "@latticexyz/store-sync"; +import { StorageAdapterLog } from "@latticexyz/store-sync"; type Props = { - type: StorageOperation["type"]; + type: StorageAdapterLog["eventName"]; }; export function EventIcon({ type }: Props) { switch (type) { - case "SetRecord": + case "StoreSetRecord": return =; - case "SetField": + case "StoreSpliceStaticData": + case "StoreSpliceDynamicData": return +; - case "DeleteRecord": + case "StoreDeleteRecord": return -; - // case "EphemeralRecord": - // return ~; + case "StoreEphemeralRecord": + return ~; default: - return assertExhaustive(type, `Unexpected storage operation type: ${type}`); + return assertExhaustive(type, `Unexpected event type: ${type}`); } } diff --git a/packages/dev-tools/src/events/EventsPage.tsx b/packages/dev-tools/src/events/EventsPage.tsx index 59e3f21888..e07a463810 100644 --- a/packages/dev-tools/src/events/EventsPage.tsx +++ b/packages/dev-tools/src/events/EventsPage.tsx @@ -1,9 +1,9 @@ import { useRef, useEffect } from "react"; import { useDevToolsContext } from "../DevToolsContext"; -import { StorageOperationsTable } from "./StorageOperationsTable"; +import { LogsTable } from "./LogsTable"; export function EventsPage() { - const { storageOperations } = useDevToolsContext(); + const { storedLogs } = useDevToolsContext(); const containerRef = useRef(null); const hoveredRef = useRef(false); const scrollBehaviorRef = useRef("auto"); @@ -13,7 +13,7 @@ export function EventsPage() { containerRef.current?.scrollIntoView({ behavior: scrollBehaviorRef.current, block: "end" }); } scrollBehaviorRef.current = "smooth"; - }, [storageOperations]); + }, [storedLogs]); return (
- +
); } diff --git a/packages/dev-tools/src/events/LogsTable.tsx b/packages/dev-tools/src/events/LogsTable.tsx new file mode 100644 index 0000000000..ac4779fb2b --- /dev/null +++ b/packages/dev-tools/src/events/LogsTable.tsx @@ -0,0 +1,72 @@ +import { StorageAdapterLog } from "@latticexyz/store-sync"; +import { EventIcon } from "./EventIcon"; +import { hexToTableId } from "@latticexyz/common"; + +// TODO: use react-table or similar for better perf with lots of logs + +type Props = { + logs: StorageAdapterLog[]; +}; + +export function LogsTable({ logs }: Props) { + return ( + + + + + + + + + + + + {logs.map((log) => { + const { namespace, name } = hexToTableId(log.args.tableId); + return ( + + + + + + + + ); + })} + +
blocktablekeyvalue
+ {log.blockNumber?.toString()} + + {namespace}:{name} + {log.args.keyTuple.join(",")} + + + {/* TODO: decode these values if we can */} + {log.eventName === "StoreSetRecord" || log.eventName === "StoreEphemeralRecord" + ? JSON.stringify({ + staticData: log.args.staticData, + encodedLengths: log.args.encodedLengths, + dynamicData: log.args.dynamicData, + }) + : null} + {log.eventName === "StoreSpliceStaticData" + ? JSON.stringify({ start: log.args.start, deleteCount: log.args.deleteCount, data: log.args.data }) + : null} + {log.eventName === "StoreSpliceDynamicData" + ? JSON.stringify({ + start: log.args.start, + deleteCount: log.args.deleteCount, + data: log.args.data, + encodedLengths: log.args.encodedLengths, + }) + : null} +
+ ); +} diff --git a/packages/dev-tools/src/events/StorageOperationsTable.tsx b/packages/dev-tools/src/events/StorageOperationsTable.tsx deleted file mode 100644 index bcf63d8876..0000000000 --- a/packages/dev-tools/src/events/StorageOperationsTable.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { StorageOperation } from "@latticexyz/store-sync"; -import { serialize } from "../serialize"; -import { EventIcon } from "./EventIcon"; -import { StoreConfig } from "@latticexyz/store"; - -// TODO: use react-table or similar for better perf with lots of logs - -type Props = { - operations: StorageOperation[]; -}; - -export function StorageOperationsTable({ operations }: Props) { - return ( - - - - - - - - - - - - {operations.map((operation) => ( - - - - - - - - ))} - -
blocktablekeyvalue
- {operation.log?.blockNumber.toString()} - - {operation.namespace}:{operation.name} - {serialize(operation.key)} - - - {operation.type === "SetRecord" ? serialize(operation.value) : null} - {operation.type === "SetField" ? serialize({ [operation.fieldName]: operation.fieldValue }) : null} -
- ); -} diff --git a/packages/dev-tools/src/summary/EventsSummary.tsx b/packages/dev-tools/src/summary/EventsSummary.tsx index 34e7bcebfb..fbc2ad87cb 100644 --- a/packages/dev-tools/src/summary/EventsSummary.tsx +++ b/packages/dev-tools/src/summary/EventsSummary.tsx @@ -1,12 +1,12 @@ import { NavButton } from "../NavButton"; import { useDevToolsContext } from "../DevToolsContext"; -import { StorageOperationsTable } from "../events/StorageOperationsTable"; +import { LogsTable } from "../events/LogsTable"; export function EventsSummary() { - const { storageOperations } = useDevToolsContext(); + const { storedLogs } = useDevToolsContext(); return ( <> - + See more diff --git a/packages/faucet/.eslintrc b/packages/faucet/.eslintrc new file mode 100644 index 0000000000..6db0063ad7 --- /dev/null +++ b/packages/faucet/.eslintrc @@ -0,0 +1,6 @@ +{ + "extends": ["../../.eslintrc"], + "rules": { + "@typescript-eslint/explicit-function-return-type": "error" + } +} diff --git a/packages/faucet/.gitignore b/packages/faucet/.gitignore new file mode 100644 index 0000000000..1521c8b765 --- /dev/null +++ b/packages/faucet/.gitignore @@ -0,0 +1 @@ +dist diff --git a/packages/faucet/.npmignore b/packages/faucet/.npmignore new file mode 100644 index 0000000000..84815f1eba --- /dev/null +++ b/packages/faucet/.npmignore @@ -0,0 +1,6 @@ +* + +!dist/** +!src/** +!package.json +!README.md diff --git a/packages/faucet/bin/faucet-server.ts b/packages/faucet/bin/faucet-server.ts new file mode 100644 index 0000000000..f6a6327a3d --- /dev/null +++ b/packages/faucet/bin/faucet-server.ts @@ -0,0 +1,56 @@ +#!/usr/bin/env node +import "dotenv/config"; +import { z } from "zod"; +import fastify from "fastify"; +import { fastifyTRPCPlugin } from "@trpc/server/adapters/fastify"; +import { ClientConfig, http, parseEther, isHex, createClient } from "viem"; +import { privateKeyToAccount } from "viem/accounts"; +import { AppRouter, createAppRouter } from "../src/createAppRouter"; +import { getChainId } from "viem/actions"; + +// TODO: refine zod type to be either CHAIN_ID or RPC_HTTP_URL/RPC_WS_URL +const env = z + .object({ + HOST: z.string().default("0.0.0.0"), + PORT: z.coerce.number().positive().default(3002), + RPC_HTTP_URL: z.string(), + FAUCET_PRIVATE_KEY: z.string().refine(isHex), + DRIP_AMOUNT_ETHER: z + .string() + .default("1") + .transform((ether) => parseEther(ether)), + }) + .parse(process.env, { + errorMap: (issue) => ({ + message: `Missing or invalid environment variable: ${issue.path.join(".")}`, + }), + }); + +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, +}); + +await server.register(import("@fastify/cors")); + +// @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 new file mode 100644 index 0000000000..9bf3daa1e7 --- /dev/null +++ b/packages/faucet/package.json @@ -0,0 +1,50 @@ +{ + "name": "@latticexyz/faucet", + "version": "2.0.0-next.8", + "description": "Faucet API for Lattice testnet", + "repository": { + "type": "git", + "url": "https://github.com/latticexyz/mud.git", + "directory": "packages/faucet" + }, + "license": "MIT", + "type": "module", + "exports": { + ".": "./dist/src/index.js" + }, + "types": "src/index.ts", + "bin": { + "faucet-server": "./dist/bin/faucet-server.js" + }, + "scripts": { + "build": "pnpm run build:js", + "build:js": "tsup", + "clean": "pnpm run clean:js", + "clean:js": "rimraf dist", + "dev": "tsup --watch", + "lint": "eslint .", + "start": "tsx bin/server", + "test": "tsc --noEmit --skipLibCheck", + "test:ci": "pnpm run test" + }, + "dependencies": { + "@fastify/cors": "^8.3.0", + "@trpc/client": "10.34.0", + "@trpc/server": "10.34.0", + "debug": "^4.3.4", + "dotenv": "^16.0.3", + "fastify": "^4.21.0", + "viem": "1.6.0", + "zod": "^3.21.4" + }, + "devDependencies": { + "@types/debug": "^4.1.7", + "tsup": "^6.7.0", + "tsx": "^3.12.6", + "vitest": "0.31.4" + }, + "publishConfig": { + "access": "public" + }, + "gitHead": "914a1e0ae4a573d685841ca2ea921435057deb8f" +} diff --git a/packages/faucet/src/createAppRouter.ts b/packages/faucet/src/createAppRouter.ts new file mode 100644 index 0000000000..6bbe28000d --- /dev/null +++ b/packages/faucet/src/createAppRouter.ts @@ -0,0 +1,42 @@ +import { z } from "zod"; +import { initTRPC } from "@trpc/server"; +import { Client, Hex, LocalAccount, formatEther, isHex } from "viem"; +import { sendTransaction } from "viem/actions"; +import { debug } from "./debug"; + +export type AppContext = { + client: Client; + faucetAccount: LocalAccount; + dripAmount: bigint; +}; + +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +export function createAppRouter() { + const t = initTRPC.context().create(); + + return t.router({ + drip: t.procedure + .input( + z.object({ + address: z.string().refine(isHex), + }) + ) + .mutation(async (opts): Promise => { + const { client, faucetAccount, dripAmount } = opts.ctx; + + const { address } = opts.input; + const tx = await sendTransaction(client, { + chain: null, + account: faucetAccount, + to: address, + value: dripAmount, + }); + + debug(`Dripped ${formatEther(dripAmount)} ETH from ${faucetAccount.address} to ${address} (tx ${tx})`); + + return tx; + }), + }); +} + +export type AppRouter = ReturnType; diff --git a/packages/faucet/src/createClient.ts b/packages/faucet/src/createClient.ts new file mode 100644 index 0000000000..661d987035 --- /dev/null +++ b/packages/faucet/src/createClient.ts @@ -0,0 +1,21 @@ +import { createTRPCProxyClient, httpBatchLink, CreateTRPCProxyClient } from "@trpc/client"; +import type { AppRouter } from "./createAppRouter"; + +type CreateClientOptions = { + /** + * tRPC endpoint URL like `https://faucet.dev.linfra.xyz/trpc`. + */ + url: string; +}; + +/** + * Creates a tRPC client to talk to a MUD faucet. + * + * @param {CreateClientOptions} options See `CreateClientOptions`. + * @returns {CreateTRPCProxyClient} A typed tRPC client. + */ +export function createClient({ url }: CreateClientOptions): CreateTRPCProxyClient { + return createTRPCProxyClient({ + links: [httpBatchLink({ url })], + }); +} diff --git a/packages/faucet/src/debug.ts b/packages/faucet/src/debug.ts new file mode 100644 index 0000000000..a6dd8eec29 --- /dev/null +++ b/packages/faucet/src/debug.ts @@ -0,0 +1,3 @@ +import createDebug from "debug"; + +export const debug = createDebug("mud:faucet"); diff --git a/packages/faucet/src/index.ts b/packages/faucet/src/index.ts new file mode 100644 index 0000000000..03493d8de2 --- /dev/null +++ b/packages/faucet/src/index.ts @@ -0,0 +1,2 @@ +export * from "./createAppRouter"; +export * from "./createClient"; diff --git a/packages/faucet/tsconfig.json b/packages/faucet/tsconfig.json new file mode 100644 index 0000000000..e590f0c026 --- /dev/null +++ b/packages/faucet/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "es2021", + "module": "esnext", + "moduleResolution": "node", + "declaration": true, + "sourceMap": true, + "outDir": "dist", + "isolatedModules": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true + } +} diff --git a/packages/faucet/tsup.config.ts b/packages/faucet/tsup.config.ts new file mode 100644 index 0000000000..385f3fc274 --- /dev/null +++ b/packages/faucet/tsup.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from "tsup"; + +export default defineConfig({ + entry: ["src/index.ts", "bin/faucet-server.ts"], + target: "esnext", + format: ["esm"], + dts: false, + sourcemap: true, + clean: true, + minify: true, +}); diff --git a/packages/protocol-parser/src/common.ts b/packages/protocol-parser/src/common.ts index cf205abafd..377e6e9267 100644 --- a/packages/protocol-parser/src/common.ts +++ b/packages/protocol-parser/src/common.ts @@ -1,4 +1,5 @@ import { DynamicAbiType, SchemaAbiType, SchemaAbiTypeToPrimitiveType, StaticAbiType } from "@latticexyz/schema-type"; +import { Hex } from "viem"; /** @deprecated use `KeySchema` or `ValueSchema` instead */ export type Schema = { @@ -29,3 +30,9 @@ export type TableRecord; value: SchemaToPrimitives; }; + +export type ValueArgs = { + staticData: Hex; + encodedLengths: Hex; + dynamicData: Hex; +}; diff --git a/packages/protocol-parser/src/decodeRecord.ts b/packages/protocol-parser/src/decodeRecord.ts index c48dc0bd24..411d93327c 100644 --- a/packages/protocol-parser/src/decodeRecord.ts +++ b/packages/protocol-parser/src/decodeRecord.ts @@ -10,7 +10,7 @@ import { decodeDynamicField } from "./decodeDynamicField"; import { decodeStaticField } from "./decodeStaticField"; import { hexToPackedCounter } from "./hexToPackedCounter"; import { staticDataLength } from "./staticDataLength"; -import { readHex } from "./readHex"; +import { readHex } from "@latticexyz/common"; /** @deprecated use `decodeValue` instead */ export function decodeRecord(valueSchema: Schema, data: Hex): readonly (StaticPrimitiveType | DynamicPrimitiveType)[] { diff --git a/packages/protocol-parser/src/decodeValueArgs.ts b/packages/protocol-parser/src/decodeValueArgs.ts new file mode 100644 index 0000000000..d9f5e5161f --- /dev/null +++ b/packages/protocol-parser/src/decodeValueArgs.ts @@ -0,0 +1,20 @@ +import { concatHex } from "viem"; +import { isStaticAbiType } from "@latticexyz/schema-type"; +import { SchemaToPrimitives, ValueArgs, ValueSchema } from "./common"; +import { decodeValue } from "./decodeValue"; +import { staticDataLength } from "./staticDataLength"; +import { readHex } from "@latticexyz/common"; + +export function decodeValueArgs( + valueSchema: TSchema, + { staticData, encodedLengths, dynamicData }: ValueArgs +): SchemaToPrimitives { + return decodeValue( + valueSchema, + concatHex([ + readHex(staticData, 0, staticDataLength(Object.values(valueSchema).filter(isStaticAbiType))), + encodedLengths, + dynamicData, + ]) + ); +} diff --git a/packages/protocol-parser/src/encodeLengths.test.ts b/packages/protocol-parser/src/encodeLengths.test.ts new file mode 100644 index 0000000000..c55204ddc6 --- /dev/null +++ b/packages/protocol-parser/src/encodeLengths.test.ts @@ -0,0 +1,10 @@ +import { describe, expect, it } from "vitest"; +import { encodeLengths } from "./encodeLengths"; + +describe("encodeLengths", () => { + it("can encode bool key tuple", () => { + expect(encodeLengths(["0x1234", "0x12345678"])).toMatchInlineSnapshot( + '"0x0000000000000000000000000000000000000004000000000200000000000006"' + ); + }); +}); diff --git a/packages/protocol-parser/src/encodeLengths.ts b/packages/protocol-parser/src/encodeLengths.ts new file mode 100644 index 0000000000..5a3bbf0827 --- /dev/null +++ b/packages/protocol-parser/src/encodeLengths.ts @@ -0,0 +1,12 @@ +import { Hex, concatHex, padHex, size } from "viem"; +import { encodeField } from "./encodeField"; + +export function encodeLengths(values: Hex[]): Hex { + const byteLengths = values.map(size).reverse(); + const totalByteLength = byteLengths.reduce((total, length) => total + BigInt(length), 0n); + + return padHex( + concatHex([...byteLengths.map((length) => encodeField("uint40", length)), encodeField("uint56", totalByteLength)]), + { size: 32, dir: "left" } + ); +} diff --git a/packages/protocol-parser/src/encodeValue.ts b/packages/protocol-parser/src/encodeValue.ts index f953ccee21..df85964b65 100644 --- a/packages/protocol-parser/src/encodeValue.ts +++ b/packages/protocol-parser/src/encodeValue.ts @@ -1,18 +1,11 @@ -import { isStaticAbiType, isDynamicAbiType } from "@latticexyz/schema-type"; -import { Hex } from "viem"; +import { Hex, concatHex } from "viem"; import { SchemaToPrimitives, ValueSchema } from "./common"; -import { encodeRecord } from "./encodeRecord"; +import { encodeValueArgs } from "./encodeValueArgs"; export function encodeValue( valueSchema: TSchema, value: SchemaToPrimitives ): Hex { - const staticFields = Object.values(valueSchema).filter(isStaticAbiType); - const dynamicFields = Object.values(valueSchema).filter(isDynamicAbiType); - - // TODO: refactor and move all encodeRecord logic into this method so we can delete encodeRecord - - // This currently assumes fields/values are ordered by static, dynamic - // TODO: make sure we preserve ordering based on value schema definition - return encodeRecord({ staticFields, dynamicFields }, Object.values(value)); + const { staticData, encodedLengths, dynamicData } = encodeValueArgs(valueSchema, value); + return concatHex([staticData, encodedLengths, dynamicData]); } diff --git a/packages/protocol-parser/src/encodeValueArgs.ts b/packages/protocol-parser/src/encodeValueArgs.ts new file mode 100644 index 0000000000..0f58a01b8a --- /dev/null +++ b/packages/protocol-parser/src/encodeValueArgs.ts @@ -0,0 +1,27 @@ +import { StaticPrimitiveType, DynamicPrimitiveType, isStaticAbiType, isDynamicAbiType } from "@latticexyz/schema-type"; +import { concatHex } from "viem"; +import { encodeField } from "./encodeField"; +import { SchemaToPrimitives, ValueArgs, ValueSchema } from "./common"; +import { encodeLengths } from "./encodeLengths"; + +export function encodeValueArgs( + valueSchema: TSchema, + value: SchemaToPrimitives +): ValueArgs { + const staticFields = Object.values(valueSchema).filter(isStaticAbiType); + const dynamicFields = Object.values(valueSchema).filter(isDynamicAbiType); + + const values = Object.values(value); + const staticValues = values.slice(0, staticFields.length) as readonly StaticPrimitiveType[]; + const dynamicValues = values.slice(staticFields.length) as readonly DynamicPrimitiveType[]; + + const encodedStaticValues = staticValues.map((value, i) => encodeField(staticFields[i], value)); + const encodedDynamicValues = dynamicValues.map((value, i) => encodeField(dynamicFields[i], value)); + const encodedLengths = encodeLengths(encodedDynamicValues); + + return { + staticData: concatHex(encodedStaticValues), + encodedLengths, + dynamicData: concatHex(encodedDynamicValues), + }; +} diff --git a/packages/protocol-parser/src/hexToPackedCounter.ts b/packages/protocol-parser/src/hexToPackedCounter.ts index c8816d15f7..d29121b7ff 100644 --- a/packages/protocol-parser/src/hexToPackedCounter.ts +++ b/packages/protocol-parser/src/hexToPackedCounter.ts @@ -2,7 +2,7 @@ import { Hex } from "viem"; import { decodeStaticField } from "./decodeStaticField"; import { decodeDynamicField } from "./decodeDynamicField"; import { InvalidHexLengthForPackedCounterError, PackedCounterLengthMismatchError } from "./errors"; -import { readHex } from "./readHex"; +import { readHex } from "@latticexyz/common"; // Keep this logic in sync with PackedCounter.sol diff --git a/packages/protocol-parser/src/index.ts b/packages/protocol-parser/src/index.ts index 805719cc39..9874481e71 100644 --- a/packages/protocol-parser/src/index.ts +++ b/packages/protocol-parser/src/index.ts @@ -7,18 +7,19 @@ export * from "./decodeKeyTuple"; export * from "./decodeRecord"; export * from "./decodeStaticField"; export * from "./decodeValue"; +export * from "./decodeValueArgs"; export * from "./encodeField"; export * from "./encodeKey"; export * from "./encodeKeyTuple"; export * from "./encodeRecord"; export * from "./encodeValue"; +export * from "./encodeValueArgs"; export * from "./errors"; export * from "./fieldLayoutToHex"; export * from "./hexToPackedCounter"; export * from "./hexToSchema"; export * from "./hexToTableSchema"; export * from "./keySchemaToHex"; -export * from "./readHex"; export * from "./schemaIndexToAbiType"; export * from "./schemaToHex"; export * from "./staticDataLength"; diff --git a/packages/store-indexer/bin/postgres-indexer.ts b/packages/store-indexer/bin/postgres-indexer.ts index 8d0ae246e8..64cc564f24 100644 --- a/packages/store-indexer/bin/postgres-indexer.ts +++ b/packages/store-indexer/bin/postgres-indexer.ts @@ -67,14 +67,14 @@ const database = drizzle(postgres(env.DATABASE_URL), { let startBlock = env.START_BLOCK; -const storageAdapter = await postgresStorage({ database, publicClient }); +const { storageAdapter, internalTables } = await postgresStorage({ database, publicClient }); // 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. try { const currentChainStates = await database .select() - .from(storageAdapter.internalTables.chain) - .where(eq(storageAdapter.internalTables.chain.chainId, chainId)) + .from(internalTables.chain) + .where(eq(internalTables.chain.chainId, chainId)) .execute(); // 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]; @@ -98,16 +98,16 @@ try { // ignore errors, this is optional } -const { latestBlockNumber$, blockStorageOperations$ } = await createStoreSync({ +const { latestBlockNumber$, storedBlockLogs$ } = await createStoreSync({ storageAdapter, publicClient, startBlock, maxBlockRange: env.MAX_BLOCK_RANGE, }); -blockStorageOperations$.subscribe(); +storedBlockLogs$.subscribe(); -combineLatest([latestBlockNumber$, blockStorageOperations$]) +combineLatest([latestBlockNumber$, storedBlockLogs$]) .pipe( filter( ([latestBlockNumber, { blockNumber: lastBlockNumberProcessed }]) => latestBlockNumber === lastBlockNumberProcessed diff --git a/packages/store-indexer/bin/sqlite-indexer.ts b/packages/store-indexer/bin/sqlite-indexer.ts index 1433768aef..7cdaa50c87 100644 --- a/packages/store-indexer/bin/sqlite-indexer.ts +++ b/packages/store-indexer/bin/sqlite-indexer.ts @@ -90,14 +90,14 @@ try { // ignore errors, this is optional } -const { latestBlockNumber$, blockStorageOperations$ } = await syncToSqlite({ +const { latestBlockNumber$, storedBlockLogs$ } = await syncToSqlite({ database, publicClient, startBlock, maxBlockRange: env.MAX_BLOCK_RANGE, }); -combineLatest([latestBlockNumber$, blockStorageOperations$]) +combineLatest([latestBlockNumber$, storedBlockLogs$]) .pipe( filter( ([latestBlockNumber, { blockNumber: lastBlockNumberProcessed }]) => latestBlockNumber === lastBlockNumberProcessed diff --git a/packages/store-indexer/package.json b/packages/store-indexer/package.json index c17410cf6b..175b8b7090 100644 --- a/packages/store-indexer/package.json +++ b/packages/store-indexer/package.json @@ -40,9 +40,9 @@ "@trpc/client": "10.34.0", "@trpc/server": "10.34.0", "@wagmi/chains": "^0.2.22", - "better-sqlite3": "^8.4.0", + "better-sqlite3": "^8.6.0", "debug": "^4.3.4", - "drizzle-orm": "^0.27.0", + "drizzle-orm": "^0.28.5", "fastify": "^4.21.0", "postgres": "^3.3.5", "rxjs": "7.5.5", diff --git a/packages/store-indexer/src/sqlite/createQueryAdapter.ts b/packages/store-indexer/src/sqlite/createQueryAdapter.ts index eae41e90c4..07d4e86949 100644 --- a/packages/store-indexer/src/sqlite/createQueryAdapter.ts +++ b/packages/store-indexer/src/sqlite/createQueryAdapter.ts @@ -1,6 +1,6 @@ import { eq } from "drizzle-orm"; import { BaseSQLiteDatabase } from "drizzle-orm/sqlite-core"; -import { createSqliteTable, chainState, getTables } from "@latticexyz/store-sync/sqlite"; +import { buildTable, chainState, getTables } from "@latticexyz/store-sync/sqlite"; import { QueryAdapter } from "@latticexyz/store-sync/trpc-indexer"; import { debug } from "../debug"; @@ -16,7 +16,7 @@ export async function createQueryAdapter(database: BaseSQLiteDatabase<"sync", an const tables = getTables(database).filter((table) => table.address === address); const tablesWithRecords = tables.map((table) => { - const sqliteTable = createSqliteTable(table); + const sqliteTable = buildTable(table); const records = database.select().from(sqliteTable).where(eq(sqliteTable.__isDeleted, false)).all(); return { ...table, diff --git a/packages/store-sync/package.json b/packages/store-sync/package.json index 74ac0cd678..ca4ce4abbc 100644 --- a/packages/store-sync/package.json +++ b/packages/store-sync/package.json @@ -56,8 +56,8 @@ "@trpc/client": "10.34.0", "@trpc/server": "10.34.0", "debug": "^4.3.4", - "drizzle-orm": "^0.27.0", - "kysely": "^0.26.1", + "drizzle-orm": "^0.28.5", + "kysely": "^0.26.3", "postgres": "^3.3.5", "rxjs": "7.5.5", "sql.js": "^1.8.0", diff --git a/packages/store-sync/src/blockLogsToStorage.test.ts b/packages/store-sync/src/blockLogsToStorage.test.ts deleted file mode 100644 index e3f134db48..0000000000 --- a/packages/store-sync/src/blockLogsToStorage.test.ts +++ /dev/null @@ -1,195 +0,0 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; -import { blockLogsToStorage } from "./blockLogsToStorage"; -import storeConfig from "@latticexyz/store/mud.config"; -import { isDefined } from "@latticexyz/common/utils"; -import { tableIdToHex } from "@latticexyz/common"; -import { StorageAdapter } from "./common"; - -const mockedCallbacks = { - registerTables: vi.fn, ReturnType>(), - getTables: vi.fn, ReturnType>(), - storeOperations: vi.fn< - Parameters, - ReturnType - >(), -}; - -const mockedDecode = blockLogsToStorage( - mockedCallbacks as any as StorageAdapter -); - -describe("blockLogsToStorage", () => { - beforeEach(() => { - vi.clearAllMocks(); - }); - - it("call setField with data properly decoded", async () => { - mockedCallbacks.getTables.mockImplementation(async ({ tables }) => { - return tables - .map((table) => { - if (table.namespace === "" && table.name === "Inventory") { - return { - ...table, - tableId: tableIdToHex("", "Inventory"), - keySchema: { - owner: "address", - item: "uint32", - itemVariant: "uint32", - } as const, - valueSchema: { - amount: "uint32", - } as const, - }; - } - }) - .filter(isDefined); - }); - - const operations = await mockedDecode({ - blockNumber: 5448n, - logs: [ - { - address: "0x5fbdb2315678afecb367f032d93f642f64180aa3", - topics: ["0x912af873e852235aae78a1d25ae9bb28b616a67c36898c53a14fd8184504ee32"], - data: "0x6d756473746f726500000000000000005461626c657300000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000496e76656e746f72790000000000000000000000000000000000000000000000000000000000000000000000000002800004010004000000000000000000000000000000000000000000000000000000001c030061030300000000000000000000000000000000000000000000000000000401000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000001600000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000056f776e657200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046974656d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b6974656d56617269616e740000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000006616d6f756e740000000000000000000000000000000000000000000000000000", - blockHash: "0x50b3e15ef79abc7c72126ca5ecbd0cf0773c48ade0528e1e3debaf9bbe3643da", - blockNumber: 5448n, - transactionHash: "0xd66a9f982ddb8da9ca15a72c5713c3390fb3a6c2f7fe81a8c9026d0ba8130189", - transactionIndex: 16, - logIndex: 53, - removed: false, - args: { - tableId: "0x6d756473746f726500000000000000005461626c657300000000000000000000", - keyTuple: ["0x00000000000000000000000000000000496e76656e746f727900000000000000"], - data: "0x0004010004000000000000000000000000000000000000000000000000000000001c030061030300000000000000000000000000000000000000000000000000000401000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000001600000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000056f776e657200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046974656d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b6974656d56617269616e740000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000006616d6f756e740000000000000000000000000000000000000000000000000000", - }, - eventName: "StoreSetRecord", - }, - { - address: "0x5fbdb2315678afecb367f032d93f642f64180aa3", - topics: ["0xd01f9f1368f831528fc9fe6442366b2b7d957fbfff3bcf7c24d9ab5fe51f8c46"], - data: "0x00000000000000000000000000000000496e76656e746f7279000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000796eb990a3f9c431c69149c7a168b91596d87f600000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000040000000800000000000000000000000000000000000000000000000000000000", - blockHash: "0x03e962e7402b2ab295b92feac342a132111dd14b0d1fd4d4a0456fdc77981577", - blockNumber: 5448n, - transactionHash: "0xa6986924609542dc4c2d81c53799d8eab47109ef34ee1e422de595e19ee9bfa4", - transactionIndex: 88, - logIndex: 88, - removed: false, - args: { - tableId: "0x00000000000000000000000000000000496e76656e746f727900000000000000", - keyTuple: [ - "0x000000000000000000000000796eb990a3f9c431c69149c7a168b91596d87f60", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000001", - ], - schemaIndex: 0, - data: "0x00000008", - }, - eventName: "StoreSetField", - }, - ], - }); - - expect(mockedCallbacks.storeOperations).toMatchInlineSnapshot(` - [MockFunction spy] { - "calls": [ - [ - { - "blockNumber": 5448n, - "operations": [ - { - "address": "0x5FbDB2315678afecb367f032d93F642f64180aa3", - "fieldName": "amount", - "fieldValue": 8, - "key": { - "item": 1, - "itemVariant": 1, - "owner": "0x796eb990A3F9C431C69149c7a168b91596D87F60", - }, - "log": { - "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", - "args": { - "data": "0x00000008", - "keyTuple": [ - "0x000000000000000000000000796eb990a3f9c431c69149c7a168b91596d87f60", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000001", - ], - "schemaIndex": 0, - "tableId": "0x00000000000000000000000000000000496e76656e746f727900000000000000", - }, - "blockHash": "0x03e962e7402b2ab295b92feac342a132111dd14b0d1fd4d4a0456fdc77981577", - "blockNumber": 5448n, - "data": "0x00000000000000000000000000000000496e76656e746f7279000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000796eb990a3f9c431c69149c7a168b91596d87f600000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000040000000800000000000000000000000000000000000000000000000000000000", - "eventName": "StoreSetField", - "logIndex": 88, - "removed": false, - "topics": [ - "0xd01f9f1368f831528fc9fe6442366b2b7d957fbfff3bcf7c24d9ab5fe51f8c46", - ], - "transactionHash": "0xa6986924609542dc4c2d81c53799d8eab47109ef34ee1e422de595e19ee9bfa4", - "transactionIndex": 88, - }, - "name": "Inventory", - "namespace": "", - "type": "SetField", - }, - ], - }, - ], - ], - "results": [ - { - "type": "return", - "value": undefined, - }, - ], - } - `); - - expect(operations).toMatchInlineSnapshot(` - { - "blockNumber": 5448n, - "operations": [ - { - "address": "0x5FbDB2315678afecb367f032d93F642f64180aa3", - "fieldName": "amount", - "fieldValue": 8, - "key": { - "item": 1, - "itemVariant": 1, - "owner": "0x796eb990A3F9C431C69149c7a168b91596D87F60", - }, - "log": { - "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", - "args": { - "data": "0x00000008", - "keyTuple": [ - "0x000000000000000000000000796eb990a3f9c431c69149c7a168b91596d87f60", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000001", - ], - "schemaIndex": 0, - "tableId": "0x00000000000000000000000000000000496e76656e746f727900000000000000", - }, - "blockHash": "0x03e962e7402b2ab295b92feac342a132111dd14b0d1fd4d4a0456fdc77981577", - "blockNumber": 5448n, - "data": "0x00000000000000000000000000000000496e76656e746f7279000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000796eb990a3f9c431c69149c7a168b91596d87f600000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000040000000800000000000000000000000000000000000000000000000000000000", - "eventName": "StoreSetField", - "logIndex": 88, - "removed": false, - "topics": [ - "0xd01f9f1368f831528fc9fe6442366b2b7d957fbfff3bcf7c24d9ab5fe51f8c46", - ], - "transactionHash": "0xa6986924609542dc4c2d81c53799d8eab47109ef34ee1e422de595e19ee9bfa4", - "transactionIndex": 88, - }, - "name": "Inventory", - "namespace": "", - "type": "SetField", - }, - ], - } - `); - }); -}); diff --git a/packages/store-sync/src/blockLogsToStorage.ts b/packages/store-sync/src/blockLogsToStorage.ts deleted file mode 100644 index e2d025f3e3..0000000000 --- a/packages/store-sync/src/blockLogsToStorage.ts +++ /dev/null @@ -1,192 +0,0 @@ -import { decodeField, decodeKeyTuple, decodeRecord, abiTypesToSchema, hexToSchema } from "@latticexyz/protocol-parser"; -import { - StoreConfig, - ConfigToKeyPrimitives as Key, - ConfigToValuePrimitives as Value, - ConfigToValuePrimitives, -} from "@latticexyz/store"; -import { decodeAbiParameters, getAddress, parseAbiParameters } from "viem"; -import { debug } from "./debug"; -import { isDefined } from "@latticexyz/common/utils"; -import { BlockLogs, StorageAdapter, StorageOperation, Table } from "./common"; -import { hexToTableId, tableIdToHex } from "@latticexyz/common"; -import storeConfig from "@latticexyz/store/mud.config"; - -// TODO: adjust when we get namespace support (https://github.com/latticexyz/mud/issues/994) and when table has namespace key (https://github.com/latticexyz/mud/issues/1201) -const schemasTable = storeConfig.tables.Tables; -const schemasTableId = tableIdToHex(storeConfig.namespace, schemasTable.name); - -export type BlockStorageOperations = { - blockNumber: BlockLogs["blockNumber"]; - operations: StorageOperation[]; -}; - -export type BlockLogsToStorageResult = ( - block: BlockLogs -) => Promise>; - -export function blockLogsToStorage({ - registerTables, - getTables, - storeOperations, -}: StorageAdapter): BlockLogsToStorageResult { - return async (block) => { - // Find table schema registration events - const newTables = block.logs - .map((log) => { - try { - if (log.eventName !== "StoreSetRecord") return; - if (log.args.tableId !== schemasTableId) return; - - // TODO: refactor encode/decode to use Record schemas - // TODO: refactor to decode key with protocol-parser utils - - const [tableId, ...otherKeys] = log.args.keyTuple; - if (otherKeys.length) { - console.warn("registerSchema event is expected to have only one key in key tuple, but got multiple", log); - } - - const table = hexToTableId(tableId); - - const valueTuple = decodeRecord(abiTypesToSchema(Object.values(schemasTable.valueSchema)), log.args.data); - const value = Object.fromEntries( - Object.keys(schemasTable.valueSchema).map((name, i) => [name, valueTuple[i]]) - ) as ConfigToValuePrimitives; - - const keySchema = hexToSchema(value.keySchema); - const valueSchema = hexToSchema(value.valueSchema); - const keyNames = decodeAbiParameters(parseAbiParameters("string[]"), value.abiEncodedKeyNames)[0]; - const fieldNames = decodeAbiParameters(parseAbiParameters("string[]"), value.abiEncodedFieldNames)[0]; - - const valueAbiTypes = [...valueSchema.staticFields, ...valueSchema.dynamicFields]; - - return { - address: log.address, - tableId, - namespace: table.namespace, - name: table.name, - keySchema: Object.fromEntries(keySchema.staticFields.map((abiType, i) => [keyNames[i], abiType])), - valueSchema: Object.fromEntries(valueAbiTypes.map((abiType, i) => [fieldNames[i], abiType])), - }; - } catch (error: unknown) { - console.error("Failed to get table from log", log, error); - } - }) - .filter(isDefined); - - // Then register tables before we start storing data in them - if (newTables.length > 0) { - await registerTables({ - blockNumber: block.blockNumber, - tables: newTables, - }); - } - - const tablesToFetch = Array.from( - new Set( - block.logs.map((log) => - JSON.stringify({ - address: getAddress(log.address), - tableId: log.args.tableId, - ...hexToTableId(log.args.tableId), - }) - ) - ) - ).map((json) => JSON.parse(json)); - - const tables = Object.fromEntries( - ( - await getTables({ - blockNumber: block.blockNumber, - tables: tablesToFetch, - }) - ).map((table) => [`${getAddress(table.address)}:${table.tableId}`, table]) - ) as Record; - - const operations = block.logs - .map((log): StorageOperation | undefined => { - try { - const table = tables[`${getAddress(log.address)}:${log.args.tableId}`]; - if (!table) { - debug("no table found for event, skipping", hexToTableId(log.args.tableId), log); - return; - } - - const keyNames = Object.keys(table.keySchema); - const keyValues = decodeKeyTuple( - { staticFields: Object.values(table.keySchema), dynamicFields: [] }, - log.args.keyTuple - ); - const key = Object.fromEntries(keyValues.map((value, i) => [keyNames[i], value])) as Key< - TConfig, - keyof TConfig["tables"] - >; - - const valueAbiTypes = Object.values(table.valueSchema); - const valueSchema = abiTypesToSchema(valueAbiTypes); - const fieldNames = Object.keys(table.valueSchema); - - // TODO: decide if we should split these up into distinct operations so the storage adapter can decide whether to combine or not - if (log.eventName === "StoreSetRecord" || log.eventName === "StoreEphemeralRecord") { - const valueTuple = decodeRecord(valueSchema, log.args.data); - const value = Object.fromEntries(fieldNames.map((name, i) => [name, valueTuple[i]])) as Value< - TConfig, - keyof TConfig["tables"] - >; - return { - log, - address: getAddress(log.address), - namespace: table.namespace, - name: table.name, - type: "SetRecord", - key, - value, - }; - } - - if (log.eventName === "StoreSetField") { - const fieldName = fieldNames[log.args.schemaIndex] as string & - keyof Value; - const fieldValue = decodeField(valueAbiTypes[log.args.schemaIndex], log.args.data) as Value< - TConfig, - keyof TConfig["tables"] - >[typeof fieldName]; - return { - log, - address: getAddress(log.address), - namespace: table.namespace, - name: table.name, - type: "SetField", - key, - fieldName, - fieldValue, - }; - } - - if (log.eventName === "StoreDeleteRecord") { - return { - log, - address: getAddress(log.address), - namespace: table.namespace, - name: table.name, - type: "DeleteRecord", - key, - }; - } - - debug("unknown store event or log, skipping", log); - return; - } catch (error: unknown) { - console.error("Failed to translate log to storage operation", log, error); - } - }) - .filter(isDefined); - - await storeOperations({ blockNumber: block.blockNumber, operations }); - - return { - blockNumber: block.blockNumber, - operations, - }; - }; -} diff --git a/packages/store-sync/src/common.ts b/packages/store-sync/src/common.ts index d903b05ba1..4927923521 100644 --- a/packages/store-sync/src/common.ts +++ b/packages/store-sync/src/common.ts @@ -1,15 +1,10 @@ import { Address, Block, Hex, Log, PublicClient } from "viem"; -import { GroupLogsByBlockNumberResult } from "@latticexyz/block-logs-stream"; -import { - StoreConfig, - ConfigToKeyPrimitives as Key, - ConfigToValuePrimitives as Value, - StoreEventsAbiItem, - StoreEventsAbi, -} from "@latticexyz/store"; +import { StoreConfig, StoreEventsAbiItem, StoreEventsAbi } from "@latticexyz/store"; +import storeConfig from "@latticexyz/store/mud.config"; import { Observable } from "rxjs"; -import { BlockStorageOperations } from "./blockLogsToStorage"; -import { KeySchema, ValueSchema, TableRecord } from "@latticexyz/protocol-parser"; +import { tableIdToHex } from "@latticexyz/common"; +import { UnionPick } from "@latticexyz/common/type-utils"; +import { KeySchema, TableRecord, ValueSchema } from "@latticexyz/protocol-parser"; export type ChainId = number; export type WorldId = `${ChainId}:${Address}`; @@ -29,52 +24,7 @@ export type Table = { export type TableWithRecords = Table & { records: TableRecord[] }; export type StoreEventsLog = Log; -export type BlockLogs = GroupLogsByBlockNumberResult[number]; - -export type BaseStorageOperation = { - log?: StoreEventsLog; - address: Hex; - namespace: TableNamespace; - name: TableName; -}; - -export type SetRecordOperation = BaseStorageOperation & { - type: "SetRecord"; -} & { - [TTable in keyof TConfig["tables"]]: { - name: TTable & string; - key: Key; - value: Value; - }; - }[keyof TConfig["tables"]]; - -export type SetFieldOperation = BaseStorageOperation & { - type: "SetField"; -} & { - [TTable in keyof TConfig["tables"]]: { - name: TTable & string; - key: Key; - } & { - [TValue in keyof Value]: { - fieldName: TValue & string; - fieldValue: Value[TValue]; - }; - }[keyof Value]; - }[keyof TConfig["tables"]]; - -export type DeleteRecordOperation = BaseStorageOperation & { - type: "DeleteRecord"; -} & { - [TTable in keyof TConfig["tables"]]: { - name: TTable & string; - key: Key; - }; - }[keyof TConfig["tables"]]; - -export type StorageOperation = - | SetFieldOperation - | SetRecordOperation - | DeleteRecordOperation; +export type BlockLogs = { blockNumber: StoreEventsLog["blockNumber"]; logs: StoreEventsLog[] }; export type SyncOptions = { /** @@ -112,22 +62,19 @@ export type SyncOptions = { }; }; -export type SyncResult = { +export type SyncResult = { latestBlock$: Observable; latestBlockNumber$: Observable; blockLogs$: Observable; - blockStorageOperations$: Observable>; + storedBlockLogs$: Observable; waitForTransaction: (tx: Hex) => Promise; }; -export type StorageAdapter = { - registerTables: (opts: { blockNumber: BlockLogs["blockNumber"]; tables: Table[] }) => Promise; - getTables: (opts: { - blockNumber: BlockLogs["blockNumber"]; - tables: Pick[]; - }) => Promise; - storeOperations: (opts: { - blockNumber: BlockLogs["blockNumber"]; - operations: StorageOperation[]; - }) => Promise; -}; +// TODO: add optional, original log to this? +export type StorageAdapterLog = Partial & UnionPick; +export type StorageAdapterBlock = { blockNumber: BlockLogs["blockNumber"]; logs: StorageAdapterLog[] }; +export type StorageAdapter = (block: StorageAdapterBlock) => Promise; + +// TODO: adjust when we get namespace support (https://github.com/latticexyz/mud/issues/994) and when table has namespace key (https://github.com/latticexyz/mud/issues/1201) +export const schemasTable = storeConfig.tables.Tables; +export const schemasTableId = tableIdToHex(storeConfig.namespace, schemasTable.name); diff --git a/packages/store-sync/src/createStoreSync.ts b/packages/store-sync/src/createStoreSync.ts index 2e0cfc0857..dcd8852df4 100644 --- a/packages/store-sync/src/createStoreSync.ts +++ b/packages/store-sync/src/createStoreSync.ts @@ -1,6 +1,13 @@ -import { ConfigToKeyPrimitives, ConfigToValuePrimitives, StoreConfig, storeEventsAbi } from "@latticexyz/store"; +import { StoreConfig, storeEventsAbi } from "@latticexyz/store"; import { Hex, TransactionReceiptNotFoundError } from "viem"; -import { SetRecordOperation, StorageAdapter, SyncOptions, SyncResult, TableWithRecords } from "./common"; +import { + StorageAdapter, + StorageAdapterBlock, + StorageAdapterLog, + SyncOptions, + SyncResult, + TableWithRecords, +} from "./common"; import { createBlockStream, blockRangeToLogs, groupLogsByBlockNumber } from "@latticexyz/block-logs-stream"; import { filter, @@ -20,16 +27,16 @@ import { scan, identity, } from "rxjs"; -import { BlockStorageOperations, blockLogsToStorage } from "./blockLogsToStorage"; import { debug as parentDebug } from "./debug"; import { createIndexerClient } from "./trpc-indexer"; import { SyncStep } from "./SyncStep"; import { chunk, isDefined } from "@latticexyz/common/utils"; +import { encodeKey, encodeValueArgs } from "@latticexyz/protocol-parser"; const debug = parentDebug.extend("createStoreSync"); type CreateStoreSyncOptions = SyncOptions & { - storageAdapter: StorageAdapter; + storageAdapter: StorageAdapter; onProgress?: (opts: { step: SyncStep; percentage: number; @@ -39,8 +46,6 @@ type CreateStoreSyncOptions = SyncOpt }) => void; }; -type CreateStoreSyncResult = SyncResult; - export async function createStoreSync({ storageAdapter, onProgress, @@ -50,7 +55,7 @@ export async function createStoreSync maxBlockRange, initialState, indexerUrl, -}: CreateStoreSyncOptions): Promise> { +}: CreateStoreSyncOptions): Promise { const initialState$ = defer( async (): Promise< | { @@ -109,7 +114,7 @@ export async function createStoreSync tap((startBlock) => debug("starting sync from block", startBlock)) ); - const initialStorageOperations$ = initialState$.pipe( + const initialLogs$ = initialState$.pipe( filter( (initialState): initialState is { blockNumber: bigint; tables: TableWithRecords[] } => initialState != null && initialState.blockNumber != null && initialState.tables.length > 0 @@ -125,27 +130,28 @@ export async function createStoreSync message: "Hydrating from snapshot", }); - await storageAdapter.registerTables({ blockNumber, tables }); - - const operations: SetRecordOperation[] = tables.flatMap((table) => - table.records.map((record) => ({ - type: "SetRecord", - address: table.address, - namespace: table.namespace, - name: table.name, - key: record.key as ConfigToKeyPrimitives, - value: record.value as ConfigToValuePrimitives, - })) + const logs: StorageAdapterLog[] = tables.flatMap((table) => + table.records.map( + (record): StorageAdapterLog => ({ + eventName: "StoreSetRecord", + address: table.address, + args: { + tableId: table.tableId, + keyTuple: encodeKey(table.keySchema, record.key), + ...encodeValueArgs(table.valueSchema, record.value), + }, + }) + ) ); // Split snapshot operations into chunks so we can update the progress callback (and ultimately render visual progress for the user). // This isn't ideal if we want to e.g. batch load these into a DB in a single DB tx, but we'll take it. // // Split into 50 equal chunks (for better `onProgress` updates) but only if we have 100+ items per chunk - const chunkSize = Math.max(100, Math.floor(operations.length / 50)); - const chunks = Array.from(chunk(operations, chunkSize)); + const chunkSize = Math.max(100, Math.floor(logs.length / 50)); + const chunks = Array.from(chunk(logs, chunkSize)); for (const [i, chunk] of chunks.entries()) { - await storageAdapter.storeOperations({ blockNumber, operations: chunk }); + await storageAdapter({ blockNumber, logs: chunk }); onProgress?.({ step: SyncStep.SNAPSHOT, percentage: (i + chunk.length) / chunks.length, @@ -163,7 +169,7 @@ export async function createStoreSync message: "Hydrated from snapshot", }); - return { blockNumber, operations }; + return { blockNumber, logs }; }), shareReplay(1) ); @@ -196,12 +202,15 @@ export async function createStoreSync ); let lastBlockNumberProcessed: bigint | null = null; - const blockStorageOperations$ = concat( - initialStorageOperations$, + const storedBlockLogs$ = concat( + initialLogs$, blockLogs$.pipe( - concatMap(blockLogsToStorage(storageAdapter)), - tap(({ blockNumber, operations }) => { - debug("stored", operations.length, "operations for block", blockNumber); + concatMap(async (block) => { + await storageAdapter(block); + return block; + }), + tap(({ blockNumber, logs }) => { + debug("stored", logs.length, "logs for block", blockNumber); lastBlockNumberProcessed = blockNumber; if (startBlock != null && endBlock != null) { @@ -232,8 +241,8 @@ export async function createStoreSync // keep 10 blocks worth processed transactions in memory const recentBlocksWindow = 10; // most recent block first, for ease of pulling the first one off the array - const recentBlocks$ = blockStorageOperations$.pipe( - scan( + const recentBlocks$ = storedBlockLogs$.pipe( + scan( (recentBlocks, block) => [block, ...recentBlocks].slice(0, recentBlocksWindow), [] ), @@ -249,7 +258,7 @@ export async function createStoreSync // We could potentially speed this up a tiny bit by racing to see if 1) tx exists in processed block or 2) fetch tx receipt for latest block processed const hasTransaction$ = recentBlocks$.pipe( concatMap(async (blocks) => { - const txs = blocks.flatMap((block) => block.operations.map((op) => op.log?.transactionHash).filter(isDefined)); + const txs = blocks.flatMap((block) => block.logs.map((op) => op.transactionHash).filter(isDefined)); if (txs.includes(tx)) return true; try { @@ -274,7 +283,7 @@ export async function createStoreSync latestBlock$, latestBlockNumber$, blockLogs$, - blockStorageOperations$, + storedBlockLogs$, waitForTransaction, }; } diff --git a/packages/store-sync/src/index.ts b/packages/store-sync/src/index.ts index 01c883d7e2..2e0771488d 100644 --- a/packages/store-sync/src/index.ts +++ b/packages/store-sync/src/index.ts @@ -1,4 +1,3 @@ -export * from "./blockLogsToStorage"; export * from "./common"; export * from "./createStoreSync"; export * from "./SyncStep"; diff --git a/packages/store-sync/src/isTableRegistrationLog.ts b/packages/store-sync/src/isTableRegistrationLog.ts new file mode 100644 index 0000000000..ca937f8030 --- /dev/null +++ b/packages/store-sync/src/isTableRegistrationLog.ts @@ -0,0 +1,7 @@ +import { StorageAdapterLog, schemasTableId } from "./common"; + +export function isTableRegistrationLog( + log: StorageAdapterLog +): log is StorageAdapterLog & { eventName: "StoreSetRecord" } { + return log.eventName === "StoreSetRecord" && log.args.tableId === schemasTableId; +} diff --git a/packages/store-sync/src/logToTable.test.ts b/packages/store-sync/src/logToTable.test.ts new file mode 100644 index 0000000000..53ec2a6a5e --- /dev/null +++ b/packages/store-sync/src/logToTable.test.ts @@ -0,0 +1,39 @@ +import { describe, it, expect } from "vitest"; +import { logToTable } from "./logToTable"; + +describe("logToTable", () => { + it("should convert a table registration log to table object", async () => { + expect( + logToTable({ + address: "0x3Aa5ebB10DC797CAC828524e59A333d0A371443c", + eventName: "StoreSetRecord", + args: { + tableId: "0x6d756473746f726500000000000000005461626c657300000000000000000000", + keyTuple: ["0x6d756473746f726500000000000000005461626c657300000000000000000000"], + staticData: + "0x0060030220202000000000000000000000000000000000000000000000000000002001005f000000000000000000000000000000000000000000000000000000006003025f5f5fc4c40000000000000000000000000000000000000000000000", + encodedLengths: "0x000000000000000000000000000000000000022000000000a0000000000002c0", // "0x00000000000000000000000000000000000000a00000000220000000000002c0", + dynamicData: + "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000077461626c654964000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000000b6669656c644c61796f757400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000096b6579536368656d610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b76616c7565536368656d610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012616269456e636f6465644b65794e616d657300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014616269456e636f6465644669656c644e616d6573000000000000000000000000", + }, + }) + ).toMatchInlineSnapshot(` + { + "address": "0x3Aa5ebB10DC797CAC828524e59A333d0A371443c", + "keySchema": { + "tableId": "bytes32", + }, + "name": "Tables", + "namespace": "mudstore", + "tableId": "0x6d756473746f726500000000000000005461626c657300000000000000000000", + "valueSchema": { + "abiEncodedFieldNames": "bytes", + "abiEncodedKeyNames": "bytes", + "fieldLayout": "bytes32", + "keySchema": "bytes32", + "valueSchema": "bytes32", + }, + } + `); + }); +}); diff --git a/packages/store-sync/src/logToTable.ts b/packages/store-sync/src/logToTable.ts new file mode 100644 index 0000000000..72f7f01d47 --- /dev/null +++ b/packages/store-sync/src/logToTable.ts @@ -0,0 +1,37 @@ +import { hexToSchema, decodeValue } from "@latticexyz/protocol-parser"; +import { concatHex, decodeAbiParameters, parseAbiParameters } from "viem"; +import { StorageAdapterLog, Table, schemasTable } from "./common"; +import { hexToTableId } from "@latticexyz/common"; + +// TODO: add tableToLog + +export function logToTable(log: StorageAdapterLog & { eventName: "StoreSetRecord" }): Table { + const [tableId, ...otherKeys] = log.args.keyTuple; + if (otherKeys.length) { + console.warn("registerSchema event is expected to have only one key in key tuple, but got multiple", log); + } + + const table = hexToTableId(tableId); + + const value = decodeValue( + schemasTable.valueSchema, + concatHex([log.args.staticData, log.args.encodedLengths, log.args.dynamicData]) + ); + + const keySchema = hexToSchema(value.keySchema); + const valueSchema = hexToSchema(value.valueSchema); + + const keyNames = decodeAbiParameters(parseAbiParameters("string[]"), value.abiEncodedKeyNames)[0]; + const fieldNames = decodeAbiParameters(parseAbiParameters("string[]"), value.abiEncodedFieldNames)[0]; + + const valueAbiTypes = [...valueSchema.staticFields, ...valueSchema.dynamicFields]; + + return { + address: log.address, + tableId, + namespace: table.namespace, + name: table.name, + keySchema: Object.fromEntries(keySchema.staticFields.map((abiType, i) => [keyNames[i], abiType])), + valueSchema: Object.fromEntries(valueAbiTypes.map((abiType, i) => [fieldNames[i], abiType])), + }; +} diff --git a/packages/store-sync/src/postgres/buildColumn.ts b/packages/store-sync/src/postgres/buildColumn.ts index 86ea59c268..d9ffa6db99 100644 --- a/packages/store-sync/src/postgres/buildColumn.ts +++ b/packages/store-sync/src/postgres/buildColumn.ts @@ -1,9 +1,10 @@ -import { AnyPgColumnBuilder, boolean, text } from "drizzle-orm/pg-core"; +import { boolean, text } from "drizzle-orm/pg-core"; import { SchemaAbiType } from "@latticexyz/schema-type"; import { assertExhaustive } from "@latticexyz/common/utils"; import { asAddress, asBigInt, asHex, asJson, asNumber } from "./columnTypes"; -export function buildColumn(name: string, schemaAbiType: SchemaAbiType): AnyPgColumnBuilder { +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +export function buildColumn(name: string, schemaAbiType: SchemaAbiType) { switch (schemaAbiType) { case "bool": return boolean(name); diff --git a/packages/store-sync/src/postgres/buildTable.test.ts b/packages/store-sync/src/postgres/buildTable.test.ts index 5d9d192a88..b13b991393 100644 --- a/packages/store-sync/src/postgres/buildTable.test.ts +++ b/packages/store-sync/src/postgres/buildTable.test.ts @@ -13,35 +13,131 @@ describe("buildTable", () => { expect(table).toMatchInlineSnapshot(` PgTable { + "__dynamicData": PgCustomColumn { + "columnType": "PgCustomColumn", + "config": { + "columnType": "PgCustomColumn", + "customTypeParams": { + "dataType": [Function], + "fromDriver": [Function], + "toDriver": [Function], + }, + "dataType": "custom", + "default": undefined, + "fieldConfig": undefined, + "hasDefault": false, + "isUnique": false, + "name": "__dynamicData", + "notNull": false, + "primaryKey": false, + "uniqueName": "users___dynamicData_unique", + "uniqueType": undefined, + }, + "dataType": "custom", + "default": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "mapFrom": [Function], + "mapTo": [Function], + "name": "__dynamicData", + "notNull": false, + "primary": false, + "sqlName": "bytea", + "table": [Circular], + "uniqueName": "users___dynamicData_unique", + "uniqueType": undefined, + }, + "__encodedLengths": PgCustomColumn { + "columnType": "PgCustomColumn", + "config": { + "columnType": "PgCustomColumn", + "customTypeParams": { + "dataType": [Function], + "fromDriver": [Function], + "toDriver": [Function], + }, + "dataType": "custom", + "default": undefined, + "fieldConfig": undefined, + "hasDefault": false, + "isUnique": false, + "name": "__encodedLengths", + "notNull": false, + "primaryKey": false, + "uniqueName": "users___encodedLengths_unique", + "uniqueType": undefined, + }, + "dataType": "custom", + "default": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "mapFrom": [Function], + "mapTo": [Function], + "name": "__encodedLengths", + "notNull": false, + "primary": false, + "sqlName": "bytea", + "table": [Circular], + "uniqueName": "users___encodedLengths_unique", + "uniqueType": undefined, + }, "__isDeleted": PgBoolean { + "columnType": "PgBoolean", "config": { + "columnType": "PgBoolean", + "dataType": "boolean", "default": undefined, + "hasDefault": false, + "isUnique": false, "name": "__isDeleted", "notNull": true, "primaryKey": false, + "uniqueName": "users___isDeleted_unique", + "uniqueType": undefined, }, + "dataType": "boolean", "default": undefined, - "hasDefault": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, "name": "__isDeleted", "notNull": true, "primary": false, "table": [Circular], + "uniqueName": "users___isDeleted_unique", + "uniqueType": undefined, }, "__key": PgCustomColumn { + "columnType": "PgCustomColumn", "config": { + "columnType": "PgCustomColumn", "customTypeParams": { "dataType": [Function], "fromDriver": [Function], "toDriver": [Function], }, + "dataType": "custom", "default": undefined, "fieldConfig": undefined, + "hasDefault": false, + "isUnique": false, "name": "__key", "notNull": true, "primaryKey": true, + "uniqueName": "users___key_unique", + "uniqueType": undefined, }, + "dataType": "custom", "default": undefined, - "hasDefault": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, "mapFrom": [Function], "mapTo": [Function], "name": "__key", @@ -49,22 +145,35 @@ describe("buildTable", () => { "primary": true, "sqlName": "bytea", "table": [Circular], + "uniqueName": "users___key_unique", + "uniqueType": undefined, }, "__lastUpdatedBlockNumber": PgCustomColumn { + "columnType": "PgCustomColumn", "config": { + "columnType": "PgCustomColumn", "customTypeParams": { "dataType": [Function], "fromDriver": [Function], "toDriver": [Function], }, + "dataType": "custom", "default": undefined, "fieldConfig": undefined, + "hasDefault": false, + "isUnique": false, "name": "__lastUpdatedBlockNumber", "notNull": true, "primaryKey": false, + "uniqueName": "users___lastUpdatedBlockNumber_unique", + "uniqueType": undefined, }, + "dataType": "custom", "default": undefined, - "hasDefault": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, "mapFrom": [Function], "mapTo": [Function], "name": "__lastUpdatedBlockNumber", @@ -72,22 +181,71 @@ describe("buildTable", () => { "primary": false, "sqlName": "numeric", "table": [Circular], + "uniqueName": "users___lastUpdatedBlockNumber_unique", + "uniqueType": undefined, + }, + "__staticData": PgCustomColumn { + "columnType": "PgCustomColumn", + "config": { + "columnType": "PgCustomColumn", + "customTypeParams": { + "dataType": [Function], + "fromDriver": [Function], + "toDriver": [Function], + }, + "dataType": "custom", + "default": undefined, + "fieldConfig": undefined, + "hasDefault": false, + "isUnique": false, + "name": "__staticData", + "notNull": false, + "primaryKey": false, + "uniqueName": "users___staticData_unique", + "uniqueType": undefined, + }, + "dataType": "custom", + "default": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "mapFrom": [Function], + "mapTo": [Function], + "name": "__staticData", + "notNull": false, + "primary": false, + "sqlName": "bytea", + "table": [Circular], + "uniqueName": "users___staticData_unique", + "uniqueType": undefined, }, "addr": PgCustomColumn { + "columnType": "PgCustomColumn", "config": { + "columnType": "PgCustomColumn", "customTypeParams": { "dataType": [Function], "fromDriver": [Function], "toDriver": [Function], }, + "dataType": "custom", "default": undefined, "fieldConfig": undefined, + "hasDefault": false, + "isUnique": false, "name": "addr", "notNull": true, "primaryKey": false, + "uniqueName": "users_addr_unique", + "uniqueType": undefined, }, + "dataType": "custom", "default": undefined, - "hasDefault": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, "mapFrom": [Function], "mapTo": [Function], "name": "addr", @@ -95,38 +253,63 @@ describe("buildTable", () => { "primary": false, "sqlName": "bytea", "table": [Circular], + "uniqueName": "users_addr_unique", + "uniqueType": undefined, }, "name": PgText { + "columnType": "PgText", "config": { + "columnType": "PgText", + "dataType": "string", "default": undefined, - "enumValues": [], + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, "name": "name", "notNull": true, "primaryKey": false, + "uniqueName": "users_name_unique", + "uniqueType": undefined, }, + "dataType": "string", "default": undefined, - "enumValues": [], - "hasDefault": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, "name": "name", "notNull": true, "primary": false, "table": [Circular], + "uniqueName": "users_name_unique", + "uniqueType": undefined, }, "x": PgCustomColumn { + "columnType": "PgCustomColumn", "config": { + "columnType": "PgCustomColumn", "customTypeParams": { "dataType": [Function], "fromDriver": [Function], "toDriver": [Function], }, + "dataType": "custom", "default": undefined, "fieldConfig": undefined, + "hasDefault": false, + "isUnique": false, "name": "x", "notNull": true, "primaryKey": false, + "uniqueName": "users_x_unique", + "uniqueType": undefined, }, + "dataType": "custom", "default": undefined, - "hasDefault": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, "mapFrom": [Function], "mapTo": [Function], "name": "x", @@ -134,22 +317,35 @@ describe("buildTable", () => { "primary": false, "sqlName": "integer", "table": [Circular], + "uniqueName": "users_x_unique", + "uniqueType": undefined, }, "y": PgCustomColumn { + "columnType": "PgCustomColumn", "config": { + "columnType": "PgCustomColumn", "customTypeParams": { "dataType": [Function], "fromDriver": [Function], "toDriver": [Function], }, + "dataType": "custom", "default": undefined, "fieldConfig": undefined, + "hasDefault": false, + "isUnique": false, "name": "y", "notNull": true, "primaryKey": false, + "uniqueName": "users_y_unique", + "uniqueType": undefined, }, + "dataType": "custom", "default": undefined, - "hasDefault": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, "mapFrom": [Function], "mapTo": [Function], "name": "y", @@ -157,40 +353,138 @@ describe("buildTable", () => { "primary": false, "sqlName": "integer", "table": [Circular], + "uniqueName": "users_y_unique", + "uniqueType": undefined, }, Symbol(drizzle:Name): "users", Symbol(drizzle:OriginalName): "users", Symbol(drizzle:Schema): "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test", Symbol(drizzle:Columns): { + "__dynamicData": PgCustomColumn { + "columnType": "PgCustomColumn", + "config": { + "columnType": "PgCustomColumn", + "customTypeParams": { + "dataType": [Function], + "fromDriver": [Function], + "toDriver": [Function], + }, + "dataType": "custom", + "default": undefined, + "fieldConfig": undefined, + "hasDefault": false, + "isUnique": false, + "name": "__dynamicData", + "notNull": false, + "primaryKey": false, + "uniqueName": "users___dynamicData_unique", + "uniqueType": undefined, + }, + "dataType": "custom", + "default": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "mapFrom": [Function], + "mapTo": [Function], + "name": "__dynamicData", + "notNull": false, + "primary": false, + "sqlName": "bytea", + "table": [Circular], + "uniqueName": "users___dynamicData_unique", + "uniqueType": undefined, + }, + "__encodedLengths": PgCustomColumn { + "columnType": "PgCustomColumn", + "config": { + "columnType": "PgCustomColumn", + "customTypeParams": { + "dataType": [Function], + "fromDriver": [Function], + "toDriver": [Function], + }, + "dataType": "custom", + "default": undefined, + "fieldConfig": undefined, + "hasDefault": false, + "isUnique": false, + "name": "__encodedLengths", + "notNull": false, + "primaryKey": false, + "uniqueName": "users___encodedLengths_unique", + "uniqueType": undefined, + }, + "dataType": "custom", + "default": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "mapFrom": [Function], + "mapTo": [Function], + "name": "__encodedLengths", + "notNull": false, + "primary": false, + "sqlName": "bytea", + "table": [Circular], + "uniqueName": "users___encodedLengths_unique", + "uniqueType": undefined, + }, "__isDeleted": PgBoolean { + "columnType": "PgBoolean", "config": { + "columnType": "PgBoolean", + "dataType": "boolean", "default": undefined, + "hasDefault": false, + "isUnique": false, "name": "__isDeleted", "notNull": true, "primaryKey": false, + "uniqueName": "users___isDeleted_unique", + "uniqueType": undefined, }, + "dataType": "boolean", "default": undefined, - "hasDefault": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, "name": "__isDeleted", "notNull": true, "primary": false, "table": [Circular], + "uniqueName": "users___isDeleted_unique", + "uniqueType": undefined, }, "__key": PgCustomColumn { + "columnType": "PgCustomColumn", "config": { + "columnType": "PgCustomColumn", "customTypeParams": { "dataType": [Function], "fromDriver": [Function], "toDriver": [Function], }, + "dataType": "custom", "default": undefined, "fieldConfig": undefined, + "hasDefault": false, + "isUnique": false, "name": "__key", "notNull": true, "primaryKey": true, + "uniqueName": "users___key_unique", + "uniqueType": undefined, }, + "dataType": "custom", "default": undefined, - "hasDefault": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, "mapFrom": [Function], "mapTo": [Function], "name": "__key", @@ -198,22 +492,35 @@ describe("buildTable", () => { "primary": true, "sqlName": "bytea", "table": [Circular], + "uniqueName": "users___key_unique", + "uniqueType": undefined, }, "__lastUpdatedBlockNumber": PgCustomColumn { + "columnType": "PgCustomColumn", "config": { + "columnType": "PgCustomColumn", "customTypeParams": { "dataType": [Function], "fromDriver": [Function], "toDriver": [Function], }, + "dataType": "custom", "default": undefined, "fieldConfig": undefined, + "hasDefault": false, + "isUnique": false, "name": "__lastUpdatedBlockNumber", "notNull": true, "primaryKey": false, + "uniqueName": "users___lastUpdatedBlockNumber_unique", + "uniqueType": undefined, }, + "dataType": "custom", "default": undefined, - "hasDefault": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, "mapFrom": [Function], "mapTo": [Function], "name": "__lastUpdatedBlockNumber", @@ -221,22 +528,71 @@ describe("buildTable", () => { "primary": false, "sqlName": "numeric", "table": [Circular], + "uniqueName": "users___lastUpdatedBlockNumber_unique", + "uniqueType": undefined, + }, + "__staticData": PgCustomColumn { + "columnType": "PgCustomColumn", + "config": { + "columnType": "PgCustomColumn", + "customTypeParams": { + "dataType": [Function], + "fromDriver": [Function], + "toDriver": [Function], + }, + "dataType": "custom", + "default": undefined, + "fieldConfig": undefined, + "hasDefault": false, + "isUnique": false, + "name": "__staticData", + "notNull": false, + "primaryKey": false, + "uniqueName": "users___staticData_unique", + "uniqueType": undefined, + }, + "dataType": "custom", + "default": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "mapFrom": [Function], + "mapTo": [Function], + "name": "__staticData", + "notNull": false, + "primary": false, + "sqlName": "bytea", + "table": [Circular], + "uniqueName": "users___staticData_unique", + "uniqueType": undefined, }, "addr": PgCustomColumn { + "columnType": "PgCustomColumn", "config": { + "columnType": "PgCustomColumn", "customTypeParams": { "dataType": [Function], "fromDriver": [Function], "toDriver": [Function], }, + "dataType": "custom", "default": undefined, "fieldConfig": undefined, + "hasDefault": false, + "isUnique": false, "name": "addr", "notNull": true, "primaryKey": false, + "uniqueName": "users_addr_unique", + "uniqueType": undefined, }, + "dataType": "custom", "default": undefined, - "hasDefault": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, "mapFrom": [Function], "mapTo": [Function], "name": "addr", @@ -244,38 +600,63 @@ describe("buildTable", () => { "primary": false, "sqlName": "bytea", "table": [Circular], + "uniqueName": "users_addr_unique", + "uniqueType": undefined, }, "name": PgText { + "columnType": "PgText", "config": { + "columnType": "PgText", + "dataType": "string", "default": undefined, - "enumValues": [], + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, "name": "name", "notNull": true, "primaryKey": false, + "uniqueName": "users_name_unique", + "uniqueType": undefined, }, + "dataType": "string", "default": undefined, - "enumValues": [], - "hasDefault": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, "name": "name", "notNull": true, "primary": false, "table": [Circular], + "uniqueName": "users_name_unique", + "uniqueType": undefined, }, "x": PgCustomColumn { + "columnType": "PgCustomColumn", "config": { + "columnType": "PgCustomColumn", "customTypeParams": { "dataType": [Function], "fromDriver": [Function], "toDriver": [Function], }, + "dataType": "custom", "default": undefined, "fieldConfig": undefined, + "hasDefault": false, + "isUnique": false, "name": "x", "notNull": true, "primaryKey": false, + "uniqueName": "users_x_unique", + "uniqueType": undefined, }, + "dataType": "custom", "default": undefined, - "hasDefault": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, "mapFrom": [Function], "mapTo": [Function], "name": "x", @@ -283,22 +664,35 @@ describe("buildTable", () => { "primary": false, "sqlName": "integer", "table": [Circular], + "uniqueName": "users_x_unique", + "uniqueType": undefined, }, "y": PgCustomColumn { + "columnType": "PgCustomColumn", "config": { + "columnType": "PgCustomColumn", "customTypeParams": { "dataType": [Function], "fromDriver": [Function], "toDriver": [Function], }, + "dataType": "custom", "default": undefined, "fieldConfig": undefined, + "hasDefault": false, + "isUnique": false, "name": "y", "notNull": true, "primaryKey": false, + "uniqueName": "users_y_unique", + "uniqueType": undefined, }, + "dataType": "custom", "default": undefined, - "hasDefault": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, "mapFrom": [Function], "mapTo": [Function], "name": "y", @@ -306,6 +700,8 @@ describe("buildTable", () => { "primary": false, "sqlName": "integer", "table": [Circular], + "uniqueName": "users_y_unique", + "uniqueType": undefined, }, }, Symbol(drizzle:BaseName): "users", @@ -328,35 +724,131 @@ describe("buildTable", () => { expect(table).toMatchInlineSnapshot(` PgTable { + "__dynamicData": PgCustomColumn { + "columnType": "PgCustomColumn", + "config": { + "columnType": "PgCustomColumn", + "customTypeParams": { + "dataType": [Function], + "fromDriver": [Function], + "toDriver": [Function], + }, + "dataType": "custom", + "default": undefined, + "fieldConfig": undefined, + "hasDefault": false, + "isUnique": false, + "name": "__dynamicData", + "notNull": false, + "primaryKey": false, + "uniqueName": "users___dynamicData_unique", + "uniqueType": undefined, + }, + "dataType": "custom", + "default": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "mapFrom": [Function], + "mapTo": [Function], + "name": "__dynamicData", + "notNull": false, + "primary": false, + "sqlName": "bytea", + "table": [Circular], + "uniqueName": "users___dynamicData_unique", + "uniqueType": undefined, + }, + "__encodedLengths": PgCustomColumn { + "columnType": "PgCustomColumn", + "config": { + "columnType": "PgCustomColumn", + "customTypeParams": { + "dataType": [Function], + "fromDriver": [Function], + "toDriver": [Function], + }, + "dataType": "custom", + "default": undefined, + "fieldConfig": undefined, + "hasDefault": false, + "isUnique": false, + "name": "__encodedLengths", + "notNull": false, + "primaryKey": false, + "uniqueName": "users___encodedLengths_unique", + "uniqueType": undefined, + }, + "dataType": "custom", + "default": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "mapFrom": [Function], + "mapTo": [Function], + "name": "__encodedLengths", + "notNull": false, + "primary": false, + "sqlName": "bytea", + "table": [Circular], + "uniqueName": "users___encodedLengths_unique", + "uniqueType": undefined, + }, "__isDeleted": PgBoolean { + "columnType": "PgBoolean", "config": { + "columnType": "PgBoolean", + "dataType": "boolean", "default": undefined, + "hasDefault": false, + "isUnique": false, "name": "__isDeleted", "notNull": true, "primaryKey": false, + "uniqueName": "users___isDeleted_unique", + "uniqueType": undefined, }, + "dataType": "boolean", "default": undefined, - "hasDefault": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, "name": "__isDeleted", "notNull": true, "primary": false, "table": [Circular], + "uniqueName": "users___isDeleted_unique", + "uniqueType": undefined, }, "__key": PgCustomColumn { + "columnType": "PgCustomColumn", "config": { + "columnType": "PgCustomColumn", "customTypeParams": { "dataType": [Function], "fromDriver": [Function], "toDriver": [Function], }, + "dataType": "custom", "default": undefined, "fieldConfig": undefined, + "hasDefault": false, + "isUnique": false, "name": "__key", "notNull": true, "primaryKey": true, + "uniqueName": "users___key_unique", + "uniqueType": undefined, }, + "dataType": "custom", "default": undefined, - "hasDefault": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, "mapFrom": [Function], "mapTo": [Function], "name": "__key", @@ -364,22 +856,35 @@ describe("buildTable", () => { "primary": true, "sqlName": "bytea", "table": [Circular], + "uniqueName": "users___key_unique", + "uniqueType": undefined, }, "__lastUpdatedBlockNumber": PgCustomColumn { + "columnType": "PgCustomColumn", "config": { + "columnType": "PgCustomColumn", "customTypeParams": { "dataType": [Function], "fromDriver": [Function], "toDriver": [Function], }, + "dataType": "custom", "default": undefined, "fieldConfig": undefined, + "hasDefault": false, + "isUnique": false, "name": "__lastUpdatedBlockNumber", "notNull": true, "primaryKey": false, + "uniqueName": "users___lastUpdatedBlockNumber_unique", + "uniqueType": undefined, }, + "dataType": "custom", "default": undefined, - "hasDefault": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, "mapFrom": [Function], "mapTo": [Function], "name": "__lastUpdatedBlockNumber", @@ -387,22 +892,71 @@ describe("buildTable", () => { "primary": false, "sqlName": "numeric", "table": [Circular], + "uniqueName": "users___lastUpdatedBlockNumber_unique", + "uniqueType": undefined, + }, + "__staticData": PgCustomColumn { + "columnType": "PgCustomColumn", + "config": { + "columnType": "PgCustomColumn", + "customTypeParams": { + "dataType": [Function], + "fromDriver": [Function], + "toDriver": [Function], + }, + "dataType": "custom", + "default": undefined, + "fieldConfig": undefined, + "hasDefault": false, + "isUnique": false, + "name": "__staticData", + "notNull": false, + "primaryKey": false, + "uniqueName": "users___staticData_unique", + "uniqueType": undefined, + }, + "dataType": "custom", + "default": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "mapFrom": [Function], + "mapTo": [Function], + "name": "__staticData", + "notNull": false, + "primary": false, + "sqlName": "bytea", + "table": [Circular], + "uniqueName": "users___staticData_unique", + "uniqueType": undefined, }, "addrs": PgCustomColumn { + "columnType": "PgCustomColumn", "config": { + "columnType": "PgCustomColumn", "customTypeParams": { "dataType": [Function], "fromDriver": [Function], "toDriver": [Function], }, + "dataType": "custom", "default": undefined, "fieldConfig": undefined, + "hasDefault": false, + "isUnique": false, "name": "addrs", "notNull": true, "primaryKey": false, + "uniqueName": "users_addrs_unique", + "uniqueType": undefined, }, + "dataType": "custom", "default": undefined, - "hasDefault": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, "mapFrom": [Function], "mapTo": [Function], "name": "addrs", @@ -410,40 +964,138 @@ describe("buildTable", () => { "primary": false, "sqlName": "text", "table": [Circular], + "uniqueName": "users_addrs_unique", + "uniqueType": undefined, }, Symbol(drizzle:Name): "users", Symbol(drizzle:OriginalName): "users", Symbol(drizzle:Schema): "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test", Symbol(drizzle:Columns): { + "__dynamicData": PgCustomColumn { + "columnType": "PgCustomColumn", + "config": { + "columnType": "PgCustomColumn", + "customTypeParams": { + "dataType": [Function], + "fromDriver": [Function], + "toDriver": [Function], + }, + "dataType": "custom", + "default": undefined, + "fieldConfig": undefined, + "hasDefault": false, + "isUnique": false, + "name": "__dynamicData", + "notNull": false, + "primaryKey": false, + "uniqueName": "users___dynamicData_unique", + "uniqueType": undefined, + }, + "dataType": "custom", + "default": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "mapFrom": [Function], + "mapTo": [Function], + "name": "__dynamicData", + "notNull": false, + "primary": false, + "sqlName": "bytea", + "table": [Circular], + "uniqueName": "users___dynamicData_unique", + "uniqueType": undefined, + }, + "__encodedLengths": PgCustomColumn { + "columnType": "PgCustomColumn", + "config": { + "columnType": "PgCustomColumn", + "customTypeParams": { + "dataType": [Function], + "fromDriver": [Function], + "toDriver": [Function], + }, + "dataType": "custom", + "default": undefined, + "fieldConfig": undefined, + "hasDefault": false, + "isUnique": false, + "name": "__encodedLengths", + "notNull": false, + "primaryKey": false, + "uniqueName": "users___encodedLengths_unique", + "uniqueType": undefined, + }, + "dataType": "custom", + "default": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "mapFrom": [Function], + "mapTo": [Function], + "name": "__encodedLengths", + "notNull": false, + "primary": false, + "sqlName": "bytea", + "table": [Circular], + "uniqueName": "users___encodedLengths_unique", + "uniqueType": undefined, + }, "__isDeleted": PgBoolean { + "columnType": "PgBoolean", "config": { + "columnType": "PgBoolean", + "dataType": "boolean", "default": undefined, + "hasDefault": false, + "isUnique": false, "name": "__isDeleted", "notNull": true, "primaryKey": false, + "uniqueName": "users___isDeleted_unique", + "uniqueType": undefined, }, + "dataType": "boolean", "default": undefined, - "hasDefault": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, "name": "__isDeleted", "notNull": true, "primary": false, "table": [Circular], + "uniqueName": "users___isDeleted_unique", + "uniqueType": undefined, }, "__key": PgCustomColumn { + "columnType": "PgCustomColumn", "config": { + "columnType": "PgCustomColumn", "customTypeParams": { "dataType": [Function], "fromDriver": [Function], "toDriver": [Function], }, + "dataType": "custom", "default": undefined, "fieldConfig": undefined, + "hasDefault": false, + "isUnique": false, "name": "__key", "notNull": true, "primaryKey": true, + "uniqueName": "users___key_unique", + "uniqueType": undefined, }, + "dataType": "custom", "default": undefined, - "hasDefault": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, "mapFrom": [Function], "mapTo": [Function], "name": "__key", @@ -451,22 +1103,35 @@ describe("buildTable", () => { "primary": true, "sqlName": "bytea", "table": [Circular], + "uniqueName": "users___key_unique", + "uniqueType": undefined, }, "__lastUpdatedBlockNumber": PgCustomColumn { + "columnType": "PgCustomColumn", "config": { + "columnType": "PgCustomColumn", "customTypeParams": { "dataType": [Function], "fromDriver": [Function], "toDriver": [Function], }, + "dataType": "custom", "default": undefined, "fieldConfig": undefined, + "hasDefault": false, + "isUnique": false, "name": "__lastUpdatedBlockNumber", "notNull": true, "primaryKey": false, + "uniqueName": "users___lastUpdatedBlockNumber_unique", + "uniqueType": undefined, }, + "dataType": "custom", "default": undefined, - "hasDefault": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, "mapFrom": [Function], "mapTo": [Function], "name": "__lastUpdatedBlockNumber", @@ -474,22 +1139,71 @@ describe("buildTable", () => { "primary": false, "sqlName": "numeric", "table": [Circular], + "uniqueName": "users___lastUpdatedBlockNumber_unique", + "uniqueType": undefined, + }, + "__staticData": PgCustomColumn { + "columnType": "PgCustomColumn", + "config": { + "columnType": "PgCustomColumn", + "customTypeParams": { + "dataType": [Function], + "fromDriver": [Function], + "toDriver": [Function], + }, + "dataType": "custom", + "default": undefined, + "fieldConfig": undefined, + "hasDefault": false, + "isUnique": false, + "name": "__staticData", + "notNull": false, + "primaryKey": false, + "uniqueName": "users___staticData_unique", + "uniqueType": undefined, + }, + "dataType": "custom", + "default": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "mapFrom": [Function], + "mapTo": [Function], + "name": "__staticData", + "notNull": false, + "primary": false, + "sqlName": "bytea", + "table": [Circular], + "uniqueName": "users___staticData_unique", + "uniqueType": undefined, }, "addrs": PgCustomColumn { + "columnType": "PgCustomColumn", "config": { + "columnType": "PgCustomColumn", "customTypeParams": { "dataType": [Function], "fromDriver": [Function], "toDriver": [Function], }, + "dataType": "custom", "default": undefined, "fieldConfig": undefined, + "hasDefault": false, + "isUnique": false, "name": "addrs", "notNull": true, "primaryKey": false, + "uniqueName": "users_addrs_unique", + "uniqueType": undefined, }, + "dataType": "custom", "default": undefined, - "hasDefault": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, "mapFrom": [Function], "mapTo": [Function], "name": "addrs", @@ -497,6 +1211,8 @@ describe("buildTable", () => { "primary": false, "sqlName": "text", "table": [Circular], + "uniqueName": "users_addrs_unique", + "uniqueType": undefined, }, }, Symbol(drizzle:BaseName): "users", diff --git a/packages/store-sync/src/postgres/buildTable.ts b/packages/store-sync/src/postgres/buildTable.ts index 0a9e24cd42..2fb7feb499 100644 --- a/packages/store-sync/src/postgres/buildTable.ts +++ b/packages/store-sync/src/postgres/buildTable.ts @@ -1,4 +1,4 @@ -import { AnyPgColumnBuilder, PgTableWithColumns, pgSchema } from "drizzle-orm/pg-core"; +import { PgColumnBuilderBase, PgTableWithColumns, pgSchema } from "drizzle-orm/pg-core"; import { buildColumn } from "./buildColumn"; import { Address, getAddress } from "viem"; import { transformSchemaName } from "./transformSchemaName"; @@ -6,13 +6,17 @@ import { KeySchema, ValueSchema } from "@latticexyz/protocol-parser"; // TODO: convert camel case to snake case for DB storage? export const metaColumns = { - __key: buildColumn("__key", "bytes").notNull().primaryKey(), + __key: buildColumn("__key", "bytes").primaryKey(), + __staticData: buildColumn("__staticData", "bytes"), + __encodedLengths: buildColumn("__encodedLengths", "bytes"), + __dynamicData: buildColumn("__dynamicData", "bytes"), __lastUpdatedBlockNumber: buildColumn("__lastUpdatedBlockNumber", "uint256").notNull(), // TODO: last updated block hash? __isDeleted: buildColumn("__isDeleted", "bool").notNull(), -} as const satisfies Record; +} as const satisfies Record; type PgTableFromSchema = PgTableWithColumns<{ + dialect: "pg"; name: string; schema: string; columns: { diff --git a/packages/store-sync/src/postgres/cleanDatabase.ts b/packages/store-sync/src/postgres/cleanDatabase.ts index eff82aaf50..26d22ad31c 100644 --- a/packages/store-sync/src/postgres/cleanDatabase.ts +++ b/packages/store-sync/src/postgres/cleanDatabase.ts @@ -1,11 +1,10 @@ -import { PgDatabase } from "drizzle-orm/pg-core"; +import { PgDatabase, getTableConfig } from "drizzle-orm/pg-core"; import { buildInternalTables } from "./buildInternalTables"; import { getTables } from "./getTables"; import { buildTable } from "./buildTable"; -import { getSchema } from "./getSchema"; import { isDefined } from "@latticexyz/common/utils"; import { debug } from "./debug"; -import { getTableName, sql } from "drizzle-orm"; +import { sql } from "drizzle-orm"; import { pgDialect } from "./pgDialect"; // This intentionally just cleans up known schemas/tables/rows. We could drop the database but that's scary. @@ -16,7 +15,7 @@ export async function cleanDatabase(db: PgDatabase): Promise { const tables = (await getTables(db)).map(buildTable); - const schemaNames = [...new Set(tables.map(getSchema))].filter(isDefined); + const schemaNames = [...new Set(tables.map((table) => getTableConfig(table).schema))].filter(isDefined); for (const schemaName of schemaNames) { try { @@ -28,7 +27,8 @@ export async function cleanDatabase(db: PgDatabase): Promise { } for (const internalTable of Object.values(internalTables)) { - debug(`deleting all rows from ${getSchema(internalTable)}.${getTableName(internalTable)}`); + const tableConfig = getTableConfig(internalTable); + debug(`deleting all rows from ${tableConfig.schema}.${tableConfig.name}`); await db.delete(internalTable); } } diff --git a/packages/store-sync/src/postgres/columnTypes.ts b/packages/store-sync/src/postgres/columnTypes.ts index 97fd352080..da9d9761f8 100644 --- a/packages/store-sync/src/postgres/columnTypes.ts +++ b/packages/store-sync/src/postgres/columnTypes.ts @@ -1,11 +1,9 @@ -import { customType, PgCustomColumnBuilder } from "drizzle-orm/pg-core"; -import { ColumnBuilderBaseConfig } from "drizzle-orm"; +import { customType } from "drizzle-orm/pg-core"; import superjson from "superjson"; import { Address, ByteArray, bytesToHex, getAddress, Hex, hexToBytes } from "viem"; -export const asJson = ( - name: string -): PgCustomColumnBuilder => +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +export const asJson = (name: string) => customType<{ data: TData; driverData: string }>({ dataType() { // TODO: move to json column type? if we do, we'll prob wanna choose something other than superjson since it adds one level of depth (json/meta keys) @@ -19,10 +17,8 @@ export const asJson = ( }, })(name); -export const asNumber = ( - name: string, - columnType: string -): PgCustomColumnBuilder => +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +export const asNumber = (name: string, columnType: string) => customType<{ data: number; driverData: string }>({ dataType() { return columnType; @@ -35,10 +31,8 @@ export const asNumber = ( }, })(name); -export const asBigInt = ( - name: string, - columnType: string -): PgCustomColumnBuilder => +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +export const asBigInt = (name: string, columnType: string) => customType<{ data: bigint; driverData: string }>({ dataType() { return columnType; @@ -51,9 +45,8 @@ export const asBigInt = ( }, })(name); -export const asHex = ( - name: string -): PgCustomColumnBuilder => +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +export const asHex = (name: string) => customType<{ data: Hex; driverData: ByteArray }>({ dataType() { return "bytea"; @@ -66,9 +59,8 @@ export const asHex = ( }, })(name); -export const asAddress = ( - name: string -): PgCustomColumnBuilder => +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +export const asAddress = (name: string) => customType<{ data: Address; driverData: ByteArray }>({ dataType() { return "bytea"; diff --git a/packages/store-sync/src/postgres/getSchema.test.ts b/packages/store-sync/src/postgres/getSchema.test.ts deleted file mode 100644 index c0c9a045a5..0000000000 --- a/packages/store-sync/src/postgres/getSchema.test.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { describe, it, expect } from "vitest"; -import { getSchema } from "./getSchema"; -import { pgTable, pgSchema } from "drizzle-orm/pg-core"; - -// Test to make sure getSchema matches drizzle internals. May need to update getSchema if these tests start failing. Hopefully by then, drizzle will have exposed their own getSchema method. - -describe("getSchema", () => { - it("should return schema if set", async () => { - expect(getSchema(pgTable("no schema", {}))).toBeUndefined(); - expect(getSchema(pgSchema("some schema").table("with schema", {}))).toBe("some schema"); - }); -}); diff --git a/packages/store-sync/src/postgres/getSchema.ts b/packages/store-sync/src/postgres/getSchema.ts deleted file mode 100644 index 240046fcd4..0000000000 --- a/packages/store-sync/src/postgres/getSchema.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { PgTable } from "drizzle-orm/pg-core"; - -// TODO: PR to drizzle to expose `getSchema` like `getTableName` -export function getSchema(table: PgTable): string | undefined { - return (table as any)[Symbol.for("drizzle:Schema")]; -} diff --git a/packages/store-sync/src/postgres/getTableKey.ts b/packages/store-sync/src/postgres/getTableKey.ts index 423830ce23..3c58cae80e 100644 --- a/packages/store-sync/src/postgres/getTableKey.ts +++ b/packages/store-sync/src/postgres/getTableKey.ts @@ -1,6 +1,8 @@ import { getAddress } from "viem"; import { Table } from "../common"; +import { hexToTableId } from "@latticexyz/common"; -export function getTableKey(table: Pick): string { - return `${getAddress(table.address)}:${table.namespace}:${table.name}`; +export function getTableKey({ address, tableId }: Pick): string { + const { namespace, name } = hexToTableId(tableId); + return `${getAddress(address)}:${namespace}:${name}`; } diff --git a/packages/store-sync/src/postgres/postgresStorage.test.ts b/packages/store-sync/src/postgres/postgresStorage.test.ts index 47c29b5ee9..8aa0548082 100644 --- a/packages/store-sync/src/postgres/postgresStorage.test.ts +++ b/packages/store-sync/src/postgres/postgresStorage.test.ts @@ -1,15 +1,29 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; -import { DefaultLogger } from "drizzle-orm"; +import { DefaultLogger, eq } from "drizzle-orm"; import { drizzle } from "drizzle-orm/postgres-js"; import postgres from "postgres"; -import { createPublicClient, http } from "viem"; +import { Hex, RpcLog, createPublicClient, decodeEventLog, formatLog, http } from "viem"; import { foundry } from "viem/chains"; -import { blockLogsToStorage } from "../blockLogsToStorage"; import * as transformSchemaNameExports from "./transformSchemaName"; import { getTables } from "./getTables"; import { PostgresStorageAdapter, postgresStorage } from "./postgresStorage"; import { buildTable } from "./buildTable"; +import { groupLogsByBlockNumber } from "@latticexyz/block-logs-stream"; +import { storeEventsAbi } from "@latticexyz/store"; import { StoreEventsLog } from "../common"; +import worldRpcLogs from "../../../../test-data/world-logs.json"; + +const blocks = groupLogsByBlockNumber( + worldRpcLogs.map((log) => { + const { eventName, args } = decodeEventLog({ + abi: storeEventsAbi, + data: log.data as Hex, + topics: log.topics as [Hex, ...Hex[]], + strict: true, + }); + return formatLog(log as any as RpcLog, { args, eventName: eventName as string }) as StoreEventsLog; + }) +); vi.spyOn(transformSchemaNameExports, "transformSchemaName").mockImplementation( (schemaName) => `${process.pid}_${process.env.VITEST_POOL_ID}__${schemaName}` @@ -33,104 +47,79 @@ describe("postgresStorage", async () => { }); it("should create tables and data from block log", async () => { - await blockLogsToStorage(storageAdapter)({ - blockNumber: 5448n, - logs: [ - { - address: "0x5fbdb2315678afecb367f032d93f642f64180aa3", - topics: ["0xd01f9f1368f831528fc9fe6442366b2b7d957fbfff3bcf7c24d9ab5fe51f8c46"], - data: "0x000000000000000000000000000000005265736f7572636554797065000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000496e76656e746f72790000000000000000000000000000000000000000000000000000000000000000000000000000010200000000000000000000000000000000000000000000000000000000000000", - blockHash: "0x4ad3752c86f900332e0d2d8903480e7206747d233586574d16f006eebdb5138b", - blockNumber: 2n, - transactionHash: "0xaa54bf18053cce5d4d2906538a60cb1d9958cc3c10c34b5f9fdc92fe6a6abab4", - transactionIndex: 16, - logIndex: 54, - removed: false, - args: { - tableId: "0x000000000000000000000000000000005265736f757263655479706500000000", - keyTuple: ["0x00000000000000000000000000000000496e76656e746f727900000000000000"], - schemaIndex: 0, - data: "0x02", - }, - eventName: "StoreSetField", - }, - { - address: "0x5fbdb2315678afecb367f032d93f642f64180aa3", - topics: ["0x912af873e852235aae78a1d25ae9bb28b616a67c36898c53a14fd8184504ee32"], - data: "0x6d756473746f726500000000000000005461626c657300000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000496e76656e746f72790000000000000000000000000000000000000000000000000000000000000000000000000002800004010004000000000000000000000000000000000000000000000000000000001c030061030300000000000000000000000000000000000000000000000000000401000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000001600000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000056f776e657200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046974656d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b6974656d56617269616e740000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000006616d6f756e740000000000000000000000000000000000000000000000000000", - blockHash: "0x4ad3752c86f900332e0d2d8903480e7206747d233586574d16f006eebdb5138b", - blockNumber: 2n, - transactionHash: "0xaa54bf18053cce5d4d2906538a60cb1d9958cc3c10c34b5f9fdc92fe6a6abab4", - transactionIndex: 16, - logIndex: 55, - removed: false, - args: { - tableId: "0x6d756473746f726500000000000000005461626c657300000000000000000000", - keyTuple: ["0x00000000000000000000000000000000496e76656e746f727900000000000000"], - data: "0x0004010004000000000000000000000000000000000000000000000000000000001c030061030300000000000000000000000000000000000000000000000000000401000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000001600000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000056f776e657200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046974656d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b6974656d56617269616e740000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000006616d6f756e740000000000000000000000000000000000000000000000000000", - }, - eventName: "StoreSetRecord", - }, - ] satisfies StoreEventsLog[], - }); + for (const block of blocks) { + await storageAdapter.storageAdapter(block); + } expect(await db.select().from(storageAdapter.internalTables.chain)).toMatchInlineSnapshot(` [ { "chainId": 31337, "lastError": null, - "lastUpdatedBlockNumber": 5448n, + "lastUpdatedBlockNumber": 5n, "schemaVersion": 1, }, ] `); - expect(await db.select().from(storageAdapter.internalTables.tables)).toMatchInlineSnapshot(` + expect( + await db + .select() + .from(storageAdapter.internalTables.tables) + .where(eq(storageAdapter.internalTables.tables.name, "NumberList")) + ).toMatchInlineSnapshot(` [ { "address": "0x5FbDB2315678afecb367f032d93F642f64180aa3", - "key": "0x5FbDB2315678afecb367f032d93F642f64180aa3::Inventory", - "keySchema": { - "item": "uint32", - "itemVariant": "uint32", - "owner": "address", - }, + "key": "0x5FbDB2315678afecb367f032d93F642f64180aa3::NumberList", + "keySchema": {}, "lastError": null, - "lastUpdatedBlockNumber": 5448n, - "name": "Inventory", + "lastUpdatedBlockNumber": 5n, + "name": "NumberList", "namespace": "", "schemaVersion": 1, - "tableId": "0x00000000000000000000000000000000496e76656e746f727900000000000000", + "tableId": "0x000000000000000000000000000000004e756d6265724c697374000000000000", "valueSchema": { - "amount": "uint32", + "value": "uint32[]", }, }, ] `); - const tables = await getTables(db, []); + const tables = (await getTables(db)).filter((table) => table.name === "NumberList"); expect(tables).toMatchInlineSnapshot(` [ { "address": "0x5FbDB2315678afecb367f032d93F642f64180aa3", - "keySchema": { - "item": "uint32", - "itemVariant": "uint32", - "owner": "address", - }, - "lastUpdatedBlockNumber": 5448n, - "name": "Inventory", + "keySchema": {}, + "lastUpdatedBlockNumber": 5n, + "name": "NumberList", "namespace": "", - "tableId": "0x00000000000000000000000000000000496e76656e746f727900000000000000", + "tableId": "0x000000000000000000000000000000004e756d6265724c697374000000000000", "valueSchema": { - "amount": "uint32", + "value": "uint32[]", }, }, ] `); const sqlTable = buildTable(tables[0]); - expect(await db.select().from(sqlTable)).toMatchInlineSnapshot("[]"); + expect(await db.select().from(sqlTable)).toMatchInlineSnapshot(` + [ + { + "__dynamicData": "0x000001a400000045", + "__encodedLengths": "0x0000000000000000000000000000000000000000000000000800000000000008", + "__isDeleted": false, + "__key": "0x", + "__lastUpdatedBlockNumber": 5n, + "__staticData": null, + "value": [ + 420, + 69, + ], + }, + ] + `); await storageAdapter.cleanUp(); }); diff --git a/packages/store-sync/src/postgres/postgresStorage.ts b/packages/store-sync/src/postgres/postgresStorage.ts index 1937a9796a..7e61e5f5d7 100644 --- a/packages/store-sync/src/postgres/postgresStorage.ts +++ b/packages/store-sync/src/postgres/postgresStorage.ts @@ -1,21 +1,24 @@ -import { PublicClient, concatHex, encodeAbiParameters } from "viem"; +import { Hex, PublicClient, concatHex } from "viem"; import { PgDatabase, QueryResultHKT } from "drizzle-orm/pg-core"; import { eq, inArray } from "drizzle-orm"; import { buildTable } from "./buildTable"; -import { schemaToDefaults } from "../schemaToDefaults"; import { StoreConfig } from "@latticexyz/store"; import { debug } from "./debug"; import { buildInternalTables } from "./buildInternalTables"; import { getTables } from "./getTables"; import { schemaVersion } from "./schemaVersion"; -import { tableIdToHex } from "@latticexyz/common"; +import { hexToTableId, spliceHex, tableIdToHex } from "@latticexyz/common"; import { setupTables } from "./setupTables"; import { getTableKey } from "./getTableKey"; -import { StorageAdapter } from "../common"; +import { StorageAdapter, StorageAdapterBlock } from "../common"; +import { isTableRegistrationLog } from "../isTableRegistrationLog"; +import { logToTable } from "../logToTable"; +import { decodeKey, decodeValueArgs } from "@latticexyz/protocol-parser"; // Currently assumes one DB per chain ID -export type PostgresStorageAdapter = StorageAdapter & { +export type PostgresStorageAdapter = { + storageAdapter: StorageAdapter; internalTables: ReturnType; cleanUp: () => Promise; }; @@ -27,7 +30,7 @@ export async function postgresStorage database: PgDatabase; publicClient: PublicClient; config?: TConfig; -}): Promise> { +}): Promise { const cleanUp: (() => Promise)[] = []; const chainId = publicClient.chain?.id ?? (await publicClient.getChainId()); @@ -35,153 +38,229 @@ export async function postgresStorage const internalTables = buildInternalTables(); cleanUp.push(await setupTables(database, Object.values(internalTables))); - const storageAdapter = { - async registerTables({ blockNumber, tables }) { - const sqlTables = tables.map((table) => - buildTable({ - address: table.address, - namespace: table.namespace, - name: table.name, - keySchema: table.keySchema, - valueSchema: table.valueSchema, - }) - ); + async function postgresStorageAdapter({ blockNumber, logs }: StorageAdapterBlock): Promise { + const newTables = logs.filter(isTableRegistrationLog).map(logToTable); + const newSqlTables = newTables.map((table) => + buildTable({ + address: table.address, + namespace: table.namespace, + name: table.name, + keySchema: table.keySchema, + valueSchema: table.valueSchema, + }) + ); - cleanUp.push(await setupTables(database, sqlTables)); + cleanUp.push(await setupTables(database, newSqlTables)); - await database.transaction(async (tx) => { - for (const table of tables) { - await tx - .insert(internalTables.tables) - .values({ - schemaVersion, - key: getTableKey(table), - address: table.address, - tableId: tableIdToHex(table.namespace, table.name), - namespace: table.namespace, - name: table.name, - keySchema: table.keySchema, - valueSchema: table.valueSchema, - lastUpdatedBlockNumber: blockNumber, - }) - .onConflictDoNothing() - .execute(); - } - }); - }, - async getTables({ tables }) { - // TODO: fetch any missing schemas from RPC - // TODO: cache schemas in memory? - return getTables(database, tables.map(getTableKey)); - }, - async storeOperations({ blockNumber, operations }) { - // This is currently parallelized per world (each world has its own database). - // This may need to change if we decide to put multiple worlds into one DB (e.g. a namespace per world, but all under one DB). - // If so, we'll probably want to wrap the entire block worth of operations in a transaction. + await database.transaction(async (tx) => { + for (const table of newTables) { + await tx + .insert(internalTables.tables) + .values({ + schemaVersion, + key: getTableKey(table), + address: table.address, + tableId: tableIdToHex(table.namespace, table.name), + namespace: table.namespace, + name: table.name, + keySchema: table.keySchema, + valueSchema: table.valueSchema, + lastUpdatedBlockNumber: blockNumber, + }) + .onConflictDoNothing() + .execute(); + } + }); + + const tables = await getTables( + database, + logs.map((log) => getTableKey({ address: log.address, tableId: log.args.tableId })) + ); + + // This is currently parallelized per world (each world has its own database). + // This may need to change if we decide to put multiple worlds into one DB (e.g. a namespace per world, but all under one DB). + // If so, we'll probably want to wrap the entire block worth of operations in a transaction. - const tables = await getTables(database, operations.map(getTableKey)); + await database.transaction(async (tx) => { + const tablesWithOperations = tables.filter((table) => + logs.some((log) => getTableKey({ address: log.address, tableId: log.args.tableId }) === getTableKey(table)) + ); + if (tablesWithOperations.length) { + await tx + .update(internalTables.tables) + .set({ lastUpdatedBlockNumber: blockNumber }) + .where(inArray(internalTables.tables.key, [...new Set(tablesWithOperations.map(getTableKey))])) + .execute(); + } - await database.transaction(async (tx) => { - const tablesWithOperations = tables.filter((table) => - operations.some((op) => getTableKey(op) === getTableKey(table)) + for (const log of logs) { + const table = tables.find( + (table) => getTableKey(table) === getTableKey({ address: log.address, tableId: log.args.tableId }) ); - if (tablesWithOperations.length) { - await tx - .update(internalTables.tables) - .set({ lastUpdatedBlockNumber: blockNumber }) - .where(inArray(internalTables.tables.key, [...new Set(tablesWithOperations.map(getTableKey))])) - .execute(); + if (!table) { + const { namespace, name } = hexToTableId(log.args.tableId); + debug(`table ${namespace}:${name} not found, skipping log`, log); + continue; } - for (const operation of operations) { - const table = tables.find((table) => getTableKey(table) === getTableKey(operation)); - if (!table) { - debug(`table ${operation.namespace}:${operation.name} not found, skipping operation`, operation); - continue; - } - - const sqlTable = buildTable(table); - const key = concatHex( - Object.entries(table.keySchema).map(([keyName, type]) => - encodeAbiParameters([{ type }], [operation.key[keyName]]) - ) - ); - - if (operation.type === "SetRecord") { - debug("SetRecord", operation); - await tx - .insert(sqlTable) - .values({ - __key: key, + const sqlTable = buildTable(table); + const uniqueKey = concatHex(log.args.keyTuple as Hex[]); + const key = decodeKey(table.keySchema, log.args.keyTuple); + + debug(log.eventName, log); + + if (log.eventName === "StoreSetRecord" || log.eventName === "StoreEphemeralRecord") { + const value = decodeValueArgs(table.valueSchema, log.args); + debug("upserting record", { + namespace: table.namespace, + name: table.name, + key, + value, + }); + await tx + .insert(sqlTable) + .values({ + __key: uniqueKey, + __staticData: log.args.staticData, + __encodedLengths: log.args.encodedLengths, + __dynamicData: log.args.dynamicData, + __lastUpdatedBlockNumber: blockNumber, + __isDeleted: false, + ...key, + ...value, + }) + .onConflictDoUpdate({ + target: sqlTable.__key, + set: { + __staticData: log.args.staticData, + __encodedLengths: log.args.encodedLengths, + __dynamicData: log.args.dynamicData, __lastUpdatedBlockNumber: blockNumber, __isDeleted: false, - ...operation.key, - ...operation.value, - }) - .onConflictDoUpdate({ - target: sqlTable.__key, - set: { - __lastUpdatedBlockNumber: blockNumber, - __isDeleted: false, - ...operation.value, - }, - }) - .execute(); - } else if (operation.type === "SetField") { - debug("SetField", operation); - await tx - .insert(sqlTable) - .values({ - __key: key, + ...value, + }, + }) + .execute(); + } else if (log.eventName === "StoreSpliceStaticData") { + // TODO: verify that this returns what we expect (doesn't error/undefined on no record) + const previousValue = (await tx.select().from(sqlTable).where(eq(sqlTable.__key, uniqueKey)).execute())[0]; + const previousStaticData = (previousValue?.__staticData as Hex) ?? "0x"; + const newStaticData = spliceHex(previousStaticData, log.args.start, log.args.deleteCount, log.args.data); + const newValue = decodeValueArgs(table.valueSchema, { + staticData: newStaticData, + encodedLengths: (previousValue?.__encodedLengths as Hex) ?? "0x", + dynamicData: (previousValue?.__dynamicData as Hex) ?? "0x", + }); + debug("upserting record via splice static", { + namespace: table.namespace, + name: table.name, + key, + previousStaticData, + newStaticData, + previousValue, + newValue, + }); + await tx + .insert(sqlTable) + .values({ + __key: uniqueKey, + __staticData: newStaticData, + __lastUpdatedBlockNumber: blockNumber, + __isDeleted: false, + ...key, + ...newValue, + }) + .onConflictDoUpdate({ + target: sqlTable.__key, + set: { + __staticData: newStaticData, __lastUpdatedBlockNumber: blockNumber, __isDeleted: false, - ...operation.key, - ...schemaToDefaults(table.valueSchema), - [operation.fieldName]: operation.fieldValue, - }) - .onConflictDoUpdate({ - target: sqlTable.__key, - set: { - __lastUpdatedBlockNumber: blockNumber, - __isDeleted: false, - [operation.fieldName]: operation.fieldValue, - }, - }) - .execute(); - } else if (operation.type === "DeleteRecord") { - // TODO: should we upsert so we at least have a DB record of when a thing was created/deleted within the same block? - debug("DeleteRecord", operation); - await tx - .update(sqlTable) - .set({ + ...newValue, + }, + }) + .execute(); + } else if (log.eventName === "StoreSpliceDynamicData") { + // TODO: verify that this returns what we expect (doesn't error/undefined on no record) + const previousValue = (await tx.select().from(sqlTable).where(eq(sqlTable.__key, uniqueKey)).execute())[0]; + const previousDynamicData = (previousValue?.__dynamicData as Hex) ?? "0x"; + const newDynamicData = spliceHex(previousDynamicData, log.args.start, log.args.deleteCount, log.args.data); + const newValue = decodeValueArgs(table.valueSchema, { + staticData: (previousValue?.__staticData as Hex) ?? "0x", + // TODO: handle unchanged encoded lengths + encodedLengths: log.args.encodedLengths, + dynamicData: newDynamicData, + }); + debug("upserting record via splice dynamic", { + namespace: table.namespace, + name: table.name, + key, + previousDynamicData, + newDynamicData, + previousValue, + newValue, + }); + await tx + .insert(sqlTable) + .values({ + __key: uniqueKey, + // TODO: handle unchanged encoded lengths + __encodedLengths: log.args.encodedLengths, + __dynamicData: newDynamicData, + __lastUpdatedBlockNumber: blockNumber, + __isDeleted: false, + ...key, + ...newValue, + }) + .onConflictDoUpdate({ + target: sqlTable.__key, + set: { + // TODO: handle unchanged encoded lengths + __encodedLengths: log.args.encodedLengths, + __dynamicData: newDynamicData, __lastUpdatedBlockNumber: blockNumber, - __isDeleted: true, - }) - .where(eq(sqlTable.__key, key)) - .execute(); - } + __isDeleted: false, + ...newValue, + }, + }) + .execute(); + } else if (log.eventName === "StoreDeleteRecord") { + // TODO: should we upsert so we at least have a DB record of when a thing was created/deleted within the same block? + debug("deleting record", { + namespace: table.namespace, + name: table.name, + key, + }); + await tx + .update(sqlTable) + .set({ + __lastUpdatedBlockNumber: blockNumber, + __isDeleted: true, + }) + .where(eq(sqlTable.__key, uniqueKey)) + .execute(); } + } - await tx - .insert(internalTables.chain) - .values({ - schemaVersion, - chainId, + await tx + .insert(internalTables.chain) + .values({ + schemaVersion, + chainId, + lastUpdatedBlockNumber: blockNumber, + }) + .onConflictDoUpdate({ + target: [internalTables.chain.schemaVersion, internalTables.chain.chainId], + set: { lastUpdatedBlockNumber: blockNumber, - }) - .onConflictDoUpdate({ - target: [internalTables.chain.schemaVersion, internalTables.chain.chainId], - set: { - lastUpdatedBlockNumber: blockNumber, - }, - }) - .execute(); - }); - }, - } as StorageAdapter; + }, + }) + .execute(); + }); + } return { - ...storageAdapter, + storageAdapter: postgresStorageAdapter, internalTables, cleanUp: async (): Promise => { for (const fn of cleanUp) { diff --git a/packages/store-sync/src/postgres/setupTables.ts b/packages/store-sync/src/postgres/setupTables.ts index 1ddcedaa6f..d15f92ce54 100644 --- a/packages/store-sync/src/postgres/setupTables.ts +++ b/packages/store-sync/src/postgres/setupTables.ts @@ -1,7 +1,6 @@ -import { AnyPgColumn, PgTableWithColumns, PgDatabase } from "drizzle-orm/pg-core"; -import { getTableColumns, getTableName, sql } from "drizzle-orm"; +import { AnyPgColumn, PgTableWithColumns, PgDatabase, getTableConfig } from "drizzle-orm/pg-core"; +import { getTableColumns, sql } from "drizzle-orm"; import { ColumnDataType } from "kysely"; -import { getSchema } from "./getSchema"; import { isDefined } from "@latticexyz/common/utils"; import { debug } from "./debug"; import { pgDialect } from "./pgDialect"; @@ -13,7 +12,7 @@ export async function setupTables( // TODO: add table to internal tables here // TODO: look up table schema and check if it matches expected schema, drop if not - const schemaNames = [...new Set(tables.map(getSchema).filter(isDefined))]; + const schemaNames = [...new Set(tables.map((table) => getTableConfig(table).schema).filter(isDefined))]; await db.transaction(async (tx) => { for (const schemaName of schemaNames) { @@ -22,12 +21,10 @@ export async function setupTables( } for (const table of tables) { - const schemaName = getSchema(table); - const scopedDb = schemaName ? pgDialect.withSchema(schemaName) : pgDialect; + const tableConfig = getTableConfig(table); + const scopedDb = tableConfig.schema ? pgDialect.withSchema(tableConfig.schema) : pgDialect; - const tableName = getTableName(table); - - let query = scopedDb.schema.createTable(tableName).ifNotExists(); + let query = scopedDb.schema.createTable(tableConfig.name).ifNotExists(); const columns = Object.values(getTableColumns(table)) as AnyPgColumn[]; for (const column of columns) { @@ -44,10 +41,10 @@ export async function setupTables( const primaryKeys = columns.filter((column) => column.primary).map((column) => column.name); if (primaryKeys.length) { - query = query.addPrimaryKeyConstraint(`${tableName}__pk`, primaryKeys as any); + query = query.addPrimaryKeyConstraint(`${tableConfig.name}__pk`, primaryKeys as any); } - debug(`creating table ${tableName} in namespace ${schemaName}`); + debug(`creating table ${tableConfig.name} in namespace ${tableConfig.schema}`); await tx.execute(sql.raw(query.compile().sql)); } }); diff --git a/packages/store-sync/src/postgres/syncToPostgres.ts b/packages/store-sync/src/postgres/syncToPostgres.ts index be649f4e78..1042ab2832 100644 --- a/packages/store-sync/src/postgres/syncToPostgres.ts +++ b/packages/store-sync/src/postgres/syncToPostgres.ts @@ -14,7 +14,7 @@ type SyncToPostgresOptions = SyncOpti startSync?: boolean; }; -type SyncToPostgresResult = SyncResult & { +type SyncToPostgresResult = SyncResult & { stopSync: () => void; }; @@ -34,9 +34,10 @@ export async function syncToPostgres( indexerUrl, initialState, startSync = true, -}: SyncToPostgresOptions): Promise> { +}: SyncToPostgresOptions): Promise { + const { storageAdapter } = await postgresStorage({ database, publicClient, config }); const storeSync = await createStoreSync({ - storageAdapter: await postgresStorage({ database, publicClient, config }), + storageAdapter, config, address, publicClient, @@ -46,7 +47,7 @@ export async function syncToPostgres( initialState, }); - const sub = startSync ? storeSync.blockStorageOperations$.subscribe() : null; + const sub = startSync ? storeSync.storedBlockLogs$.subscribe() : null; const stopSync = (): void => { sub?.unsubscribe(); }; diff --git a/packages/store-sync/src/recs/common.test-d.ts b/packages/store-sync/src/recs/common.test-d.ts index bf1e450859..a2f81ff7a4 100644 --- a/packages/store-sync/src/recs/common.test-d.ts +++ b/packages/store-sync/src/recs/common.test-d.ts @@ -4,18 +4,27 @@ import storeConfig from "@latticexyz/store/mud.config"; import { ConfigToRecsComponents } from "./common"; describe("ConfigToRecsComponents", () => { - expectTypeOf["StoreMetadata"]>().toEqualTypeOf< + expectTypeOf["Tables"]>().toEqualTypeOf< Component< { - tableName: RecsType.String; + keySchema: RecsType.String; + valueSchema: RecsType.String; + abiEncodedKeyNames: RecsType.String; abiEncodedFieldNames: RecsType.String; }, { - componentName: "StoreMetadata"; + componentName: "Tables"; // TODO: fix config namespace so it comes back as a const - tableName: `${string}:StoreMetadata`; - keySchema: { tableId: "bytes32" }; - valueSchema: { tableName: "string"; abiEncodedFieldNames: "bytes" }; + tableName: `${string}:Tables`; + keySchema: { + tableId: "bytes32"; + }; + valueSchema: { + keySchema: "bytes32"; + valueSchema: "bytes32"; + abiEncodedKeyNames: "bytes"; + abiEncodedFieldNames: "bytes"; + }; } > >(); diff --git a/packages/store-sync/src/recs/common.ts b/packages/store-sync/src/recs/common.ts index d33aeb5b93..09622f2fe8 100644 --- a/packages/store-sync/src/recs/common.ts +++ b/packages/store-sync/src/recs/common.ts @@ -14,6 +14,10 @@ export type StoreComponentMetadata = RecsMetadata & { export type ConfigToRecsComponents = { [tableName in keyof TConfig["tables"] & string]: RecsComponent< { + __staticData: RecsType.OptionalString; + __encodedLengths: RecsType.OptionalString; + __dynamicData: RecsType.OptionalString; + } & { [fieldName in keyof TConfig["tables"][tableName]["valueSchema"] & string]: RecsType & SchemaAbiTypeToRecsType; }, diff --git a/packages/store-sync/src/recs/configToRecsComponents.ts b/packages/store-sync/src/recs/configToRecsComponents.ts index 1d5b9e760e..9243f846c9 100644 --- a/packages/store-sync/src/recs/configToRecsComponents.ts +++ b/packages/store-sync/src/recs/configToRecsComponents.ts @@ -1,7 +1,7 @@ import { StoreConfig } from "@latticexyz/store"; import { SchemaAbiType } from "@latticexyz/schema-type"; import { tableIdToHex } from "@latticexyz/common"; -import { World, defineComponent } from "@latticexyz/recs"; +import { World, defineComponent, Type } from "@latticexyz/recs"; import { ConfigToRecsComponents } from "./common"; import { schemaAbiTypeToRecsType } from "./schemaAbiTypeToRecsType"; @@ -14,12 +14,17 @@ export function configToRecsComponents( tableName, defineComponent( world, - Object.fromEntries( - Object.entries(table.valueSchema).map(([fieldName, schemaAbiType]) => [ - fieldName, - schemaAbiTypeToRecsType[schemaAbiType as SchemaAbiType], - ]) - ), + { + ...Object.fromEntries( + Object.entries(table.valueSchema).map(([fieldName, schemaAbiType]) => [ + fieldName, + schemaAbiTypeToRecsType[schemaAbiType as SchemaAbiType], + ]) + ), + __staticData: Type.OptionalString, + __encodedLengths: Type.OptionalString, + __dynamicData: Type.OptionalString, + }, { id: tableIdToHex(config.namespace, tableName), metadata: { diff --git a/packages/store-sync/src/recs/decodeEntity.ts b/packages/store-sync/src/recs/decodeEntity.ts index 90167726f0..ba2c084a7f 100644 --- a/packages/store-sync/src/recs/decodeEntity.ts +++ b/packages/store-sync/src/recs/decodeEntity.ts @@ -1,7 +1,7 @@ import { Entity } from "@latticexyz/recs"; import { Hex, decodeAbiParameters } from "viem"; -import { KeySchema, SchemaToPrimitives } from "@latticexyz/protocol-parser"; import { entityToHexKeyTuple } from "./entityToHexKeyTuple"; +import { KeySchema, SchemaToPrimitives } from "@latticexyz/protocol-parser"; export function decodeEntity( keySchema: TKeySchema, diff --git a/packages/store-sync/src/recs/encodeEntity.ts b/packages/store-sync/src/recs/encodeEntity.ts index e0e155371c..bef8a33c1b 100644 --- a/packages/store-sync/src/recs/encodeEntity.ts +++ b/packages/store-sync/src/recs/encodeEntity.ts @@ -1,7 +1,7 @@ import { Entity } from "@latticexyz/recs"; import { encodeAbiParameters } from "viem"; -import { KeySchema, SchemaToPrimitives } from "@latticexyz/protocol-parser"; import { hexKeyTupleToEntity } from "./hexKeyTupleToEntity"; +import { KeySchema, SchemaToPrimitives } from "@latticexyz/protocol-parser"; export function encodeEntity( keySchema: TKeySchema, diff --git a/packages/store-sync/src/recs/getTableEntity.ts b/packages/store-sync/src/recs/getTableEntity.ts index 83b746e89b..5b92368da3 100644 --- a/packages/store-sync/src/recs/getTableEntity.ts +++ b/packages/store-sync/src/recs/getTableEntity.ts @@ -1,4 +1,4 @@ -import { Address, Hex, getAddress, stringToHex } from "viem"; +import { stringToHex } from "viem"; import { Table } from "../common"; import { Entity } from "@latticexyz/recs"; import { encodeEntity } from "./encodeEntity"; diff --git a/packages/store-sync/src/recs/recsStorage.test.ts b/packages/store-sync/src/recs/recsStorage.test.ts index 3b8e401444..4bb747b0dc 100644 --- a/packages/store-sync/src/recs/recsStorage.test.ts +++ b/packages/store-sync/src/recs/recsStorage.test.ts @@ -1,5 +1,4 @@ import { describe, expect, it } from "vitest"; -import { blockLogsToStorage } from "../blockLogsToStorage"; import { recsStorage } from "./recsStorage"; import { createWorld, getComponentEntities, getComponentValue } from "@latticexyz/recs"; import mudConfig from "../../../../e2e/packages/contracts/mud.config"; @@ -10,15 +9,18 @@ import { singletonEntity } from "./singletonEntity"; import { RpcLog, formatLog, decodeEventLog, Hex } from "viem"; import { storeEventsAbi } from "@latticexyz/store"; -const worldLogs = worldRpcLogs.map((log) => { - const { eventName, args } = decodeEventLog({ - abi: storeEventsAbi, - data: log.data as Hex, - topics: log.topics as [Hex, ...Hex[]], - strict: true, - }); - return formatLog(log as any as RpcLog, { args, eventName: eventName as string }) as StoreEventsLog; -}); +// TODO: make test-data a proper package and export this +const blocks = groupLogsByBlockNumber( + worldRpcLogs.map((log) => { + const { eventName, args } = decodeEventLog({ + abi: storeEventsAbi, + data: log.data as Hex, + topics: log.topics as [Hex, ...Hex[]], + strict: true, + }); + return formatLog(log as any as RpcLog, { args, eventName: eventName as string }) as StoreEventsLog; + }) +); describe("recsStorage", () => { it("creates components", async () => { @@ -33,8 +35,9 @@ describe("recsStorage", () => { const world = createWorld(); const { storageAdapter, components } = recsStorage({ world, config: mudConfig }); - const blocks = groupLogsByBlockNumber(worldLogs); - await Promise.all(blocks.map(async (block) => await blockLogsToStorage(storageAdapter)(block))); + for (const block of blocks) { + await storageAdapter(block); + } expect(Array.from(getComponentEntities(components.NumberList))).toMatchInlineSnapshot(` [ @@ -44,6 +47,9 @@ describe("recsStorage", () => { expect(getComponentValue(components.NumberList, singletonEntity)).toMatchInlineSnapshot(` { + "__dynamicData": "0x000001a400000045", + "__encodedLengths": "0x0000000000000000000000000000000000000000000000000800000000000008", + "__staticData": undefined, "value": [ 420, 69, diff --git a/packages/store-sync/src/recs/recsStorage.ts b/packages/store-sync/src/recs/recsStorage.ts index 9debe2652f..457eb88793 100644 --- a/packages/store-sync/src/recs/recsStorage.ts +++ b/packages/store-sync/src/recs/recsStorage.ts @@ -1,21 +1,16 @@ import { StoreConfig } from "@latticexyz/store"; import { debug } from "./debug"; -import { - ComponentValue, - World as RecsWorld, - getComponentValue, - removeComponent, - setComponent, - updateComponent, -} from "@latticexyz/recs"; -import { isDefined } from "@latticexyz/common/utils"; -import { schemaToDefaults } from "../schemaToDefaults"; +import { World as RecsWorld, getComponentValue, hasComponent, removeComponent, setComponent } from "@latticexyz/recs"; import { defineInternalComponents } from "./defineInternalComponents"; import { getTableEntity } from "./getTableEntity"; +import { hexToTableId, spliceHex } from "@latticexyz/common"; +import { decodeValueArgs } from "@latticexyz/protocol-parser"; +import { Hex } from "viem"; +import { isTableRegistrationLog } from "../isTableRegistrationLog"; +import { logToTable } from "../logToTable"; +import { hexKeyTupleToEntity } from "./hexKeyTupleToEntity"; import { ConfigToRecsComponents } from "./common"; -import { tableIdToHex } from "@latticexyz/common"; -import { encodeEntity } from "./encodeEntity"; -import { StorageAdapter } from "../common"; +import { StorageAdapter, StorageAdapterBlock } from "../common"; import { configToRecsComponents } from "./configToRecsComponents"; import { singletonEntity } from "./singletonEntity"; import storeConfig from "@latticexyz/store/mud.config"; @@ -23,18 +18,18 @@ import worldConfig from "@latticexyz/world/mud.config"; export type RecsStorageOptions = { world: RecsWorld; + // TODO: make config optional? config: TConfig; }; export type RecsStorageAdapter = { - storageAdapter: StorageAdapter; + storageAdapter: StorageAdapter; components: ConfigToRecsComponents & ConfigToRecsComponents & ConfigToRecsComponents & ReturnType; }; -// TODO: make config optional? export function recsStorage({ world, config, @@ -48,63 +43,115 @@ export function recsStorage({ ...defineInternalComponents(world), }; - const storageAdapter = { - async registerTables({ tables }) { - for (const table of tables) { - // TODO: check if table exists already and skip/warn? - setComponent(components.RegisteredTables, getTableEntity(table), { table }); + async function recsStorageAdapter({ logs }: StorageAdapterBlock): Promise { + const newTables = logs.filter(isTableRegistrationLog).map(logToTable); + for (const newTable of newTables) { + const tableEntity = getTableEntity(newTable); + if (hasComponent(components.RegisteredTables, tableEntity)) { + console.warn("table already registered, ignoring", { + newTable, + existingTable: getComponentValue(components.RegisteredTables, tableEntity)?.table, + }); + } else { + setComponent(components.RegisteredTables, tableEntity, { table: newTable }); } - }, - async getTables({ tables }) { - // TODO: fetch schema from RPC if table not found? - return tables - .map((table) => getComponentValue(components.RegisteredTables, getTableEntity(table))?.table) - .filter(isDefined); - }, - async storeOperations({ operations }) { - for (const operation of operations) { - const table = getComponentValue( - components.RegisteredTables, - getTableEntity({ - address: operation.address, - namespace: operation.namespace, - name: operation.name, - }) - )?.table; - if (!table) { - debug(`skipping update for unknown table: ${operation.namespace}:${operation.name} at ${operation.address}`); - continue; - } + } - const tableId = tableIdToHex(operation.namespace, operation.name); - const component = world.components.find((component) => component.id === tableId); - if (!component) { - debug(`skipping update for unknown component: ${tableId}. Available components: ${Object.keys(components)}`); - continue; - } + for (const log of logs) { + const { namespace, name } = hexToTableId(log.args.tableId); + const table = getComponentValue( + components.RegisteredTables, + getTableEntity({ address: log.address, namespace, name }) + )?.table; + if (!table) { + debug(`skipping update for unknown table: ${namespace}:${name} at ${log.address}`); + continue; + } + + const component = world.components.find((c) => c.id === table.tableId); + if (!component) { + debug( + `skipping update for unknown component: ${table.tableId} (${table.namespace}:${ + table.name + }). Available components: ${Object.keys(components)}` + ); + continue; + } - const entity = encodeEntity(table.keySchema, operation.key); + const entity = hexKeyTupleToEntity(log.args.keyTuple); - if (operation.type === "SetRecord") { - debug("setting component", tableId, entity, operation.value); - setComponent(component, entity, operation.value as ComponentValue); - } else if (operation.type === "SetField") { - debug("updating component", tableId, entity, { - [operation.fieldName]: operation.fieldValue, - }); - updateComponent( - component, - entity, - { [operation.fieldName]: operation.fieldValue } as ComponentValue, - schemaToDefaults(table.valueSchema) as ComponentValue - ); - } else if (operation.type === "DeleteRecord") { - debug("deleting component", tableId, entity); - removeComponent(component, entity); - } + if (log.eventName === "StoreSetRecord" || log.eventName === "StoreEphemeralRecord") { + const value = decodeValueArgs(table.valueSchema, log.args); + debug("setting component", { + namespace: table.namespace, + name: table.name, + entity, + value, + }); + setComponent(component, entity, { + ...value, + __staticData: log.args.staticData, + __encodedLengths: log.args.encodedLengths, + __dynamicData: log.args.dynamicData, + }); + } else if (log.eventName === "StoreSpliceStaticData") { + // TODO: add tests that this works when no record had been set before + const previousValue = getComponentValue(component, entity); + const previousStaticData = (previousValue?.__staticData as Hex) ?? "0x"; + const newStaticData = spliceHex(previousStaticData, log.args.start, log.args.deleteCount, log.args.data); + const newValue = decodeValueArgs(table.valueSchema, { + staticData: newStaticData, + encodedLengths: (previousValue?.__encodedLengths as Hex) ?? "0x", + dynamicData: (previousValue?.__dynamicData as Hex) ?? "0x", + }); + debug("setting component via splice static", { + namespace: table.namespace, + name: table.name, + entity, + previousStaticData, + newStaticData, + previousValue, + newValue, + }); + setComponent(component, entity, { + ...newValue, + __staticData: newStaticData, + }); + } else if (log.eventName === "StoreSpliceDynamicData") { + // TODO: add tests that this works when no record had been set before + const previousValue = getComponentValue(component, entity); + const previousDynamicData = (previousValue?.__dynamicData as Hex) ?? "0x"; + const newDynamicData = spliceHex(previousDynamicData, log.args.start, log.args.deleteCount, log.args.data); + const newValue = decodeValueArgs(table.valueSchema, { + staticData: (previousValue?.__staticData as Hex) ?? "0x", + // TODO: handle unchanged encoded lengths + encodedLengths: log.args.encodedLengths, + dynamicData: newDynamicData, + }); + debug("setting component via splice dynamic", { + namespace: table.namespace, + name: table.name, + entity, + previousDynamicData, + newDynamicData, + previousValue, + newValue, + }); + setComponent(component, entity, { + ...newValue, + __encodedLengths: log.args.encodedLengths, + __dynamicData: newDynamicData, + }); + } else if (log.eventName === "StoreDeleteRecord") { + debug("deleting component", { + namespace: table.namespace, + name: table.name, + entity, + }); + removeComponent(component, entity); } - }, - } as StorageAdapter; + } + } - return { storageAdapter, components }; + return { storageAdapter: recsStorageAdapter, components }; } diff --git a/packages/store-sync/src/recs/syncToRecs.ts b/packages/store-sync/src/recs/syncToRecs.ts index aecf0231ec..2d439e0512 100644 --- a/packages/store-sync/src/recs/syncToRecs.ts +++ b/packages/store-sync/src/recs/syncToRecs.ts @@ -12,7 +12,7 @@ type SyncToRecsOptions = SyncOptions< startSync?: boolean; }; -type SyncToRecsResult = SyncResult & { +type SyncToRecsResult = SyncResult & { components: RecsStorageAdapter["components"]; stopSync: () => void; }; @@ -52,7 +52,7 @@ export async function syncToRecs({ }, }); - const sub = startSync ? storeSync.blockStorageOperations$.subscribe() : null; + const sub = startSync ? storeSync.storedBlockLogs$.subscribe() : null; const stopSync = (): void => { sub?.unsubscribe(); }; diff --git a/packages/store-sync/src/schemaToDefaults.ts b/packages/store-sync/src/schemaToDefaults.ts index 15be27c54c..278b4c77c7 100644 --- a/packages/store-sync/src/schemaToDefaults.ts +++ b/packages/store-sync/src/schemaToDefaults.ts @@ -1,5 +1,5 @@ +import { SchemaToPrimitives, ValueSchema } from "@latticexyz/protocol-parser"; import { schemaAbiTypeToDefaultValue } from "@latticexyz/schema-type"; -import { ValueSchema, SchemaToPrimitives } from "@latticexyz/protocol-parser"; export function schemaToDefaults(valueSchema: TSchema): SchemaToPrimitives { return Object.fromEntries( diff --git a/packages/store-sync/src/sqlite/buildSqliteColumn.ts b/packages/store-sync/src/sqlite/buildColumn.ts similarity index 95% rename from packages/store-sync/src/sqlite/buildSqliteColumn.ts rename to packages/store-sync/src/sqlite/buildColumn.ts index f4e07e73cf..354600b146 100644 --- a/packages/store-sync/src/sqlite/buildSqliteColumn.ts +++ b/packages/store-sync/src/sqlite/buildColumn.ts @@ -1,9 +1,10 @@ -import { AnySQLiteColumnBuilder, blob, integer, text } from "drizzle-orm/sqlite-core"; +import { blob, integer, text } from "drizzle-orm/sqlite-core"; import { SchemaAbiType } from "@latticexyz/schema-type"; import { assertExhaustive } from "@latticexyz/common/utils"; import { address, json } from "./columnTypes"; -export function buildSqliteColumn(name: string, schemaAbiType: SchemaAbiType): AnySQLiteColumnBuilder { +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +export function buildColumn(name: string, schemaAbiType: SchemaAbiType) { switch (schemaAbiType) { case "bool": return integer(name, { mode: "boolean" }); diff --git a/packages/store-sync/src/sqlite/buildTable.test.ts b/packages/store-sync/src/sqlite/buildTable.test.ts new file mode 100644 index 0000000000..22b1eb0a42 --- /dev/null +++ b/packages/store-sync/src/sqlite/buildTable.test.ts @@ -0,0 +1,1086 @@ +import { describe, it, expect } from "vitest"; +import { buildTable } from "./buildTable"; + +describe("buildTable", () => { + it("should create table from schema", async () => { + const table = buildTable({ + address: "0xffffffffffffffffffffffffffffffffffffffff", + namespace: "test", + name: "users", + keySchema: { x: "uint32", y: "uint32" }, + valueSchema: { name: "string", addr: "address" }, + }); + + expect(table).toMatchInlineSnapshot(` + SQLiteTable { + "__dynamicData": SQLiteText { + "columnType": "SQLiteText", + "config": { + "columnType": "SQLiteText", + "dataType": "string", + "default": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "length": undefined, + "name": "__dynamicData", + "notNull": false, + "primaryKey": false, + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users___dynamicData_unique", + "uniqueType": undefined, + }, + "dataType": "string", + "default": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "length": undefined, + "name": "__dynamicData", + "notNull": false, + "primary": false, + "table": [Circular], + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users___dynamicData_unique", + "uniqueType": undefined, + }, + "__encodedLengths": SQLiteText { + "columnType": "SQLiteText", + "config": { + "columnType": "SQLiteText", + "dataType": "string", + "default": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "length": undefined, + "name": "__encodedLengths", + "notNull": false, + "primaryKey": false, + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users___encodedLengths_unique", + "uniqueType": undefined, + }, + "dataType": "string", + "default": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "length": undefined, + "name": "__encodedLengths", + "notNull": false, + "primary": false, + "table": [Circular], + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users___encodedLengths_unique", + "uniqueType": undefined, + }, + "__isDeleted": SQLiteBoolean { + "autoIncrement": false, + "columnType": "SQLiteBoolean", + "config": { + "autoIncrement": false, + "columnType": "SQLiteBoolean", + "dataType": "boolean", + "default": undefined, + "hasDefault": false, + "isUnique": false, + "mode": "boolean", + "name": "__isDeleted", + "notNull": true, + "primaryKey": false, + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users___isDeleted_unique", + "uniqueType": undefined, + }, + "dataType": "boolean", + "default": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "mode": "boolean", + "name": "__isDeleted", + "notNull": true, + "primary": false, + "table": [Circular], + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users___isDeleted_unique", + "uniqueType": undefined, + }, + "__key": SQLiteText { + "columnType": "SQLiteText", + "config": { + "columnType": "SQLiteText", + "dataType": "string", + "default": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "length": undefined, + "name": "__key", + "notNull": true, + "primaryKey": true, + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users___key_unique", + "uniqueType": undefined, + }, + "dataType": "string", + "default": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "length": undefined, + "name": "__key", + "notNull": true, + "primary": true, + "table": [Circular], + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users___key_unique", + "uniqueType": undefined, + }, + "__lastUpdatedBlockNumber": SQLiteBigInt { + "columnType": "SQLiteBigInt", + "config": { + "columnType": "SQLiteBigInt", + "dataType": "bigint", + "default": undefined, + "hasDefault": false, + "isUnique": false, + "name": "__lastUpdatedBlockNumber", + "notNull": true, + "primaryKey": false, + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users___lastUpdatedBlockNumber_unique", + "uniqueType": undefined, + }, + "dataType": "bigint", + "default": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "name": "__lastUpdatedBlockNumber", + "notNull": true, + "primary": false, + "table": [Circular], + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users___lastUpdatedBlockNumber_unique", + "uniqueType": undefined, + }, + "__staticData": SQLiteText { + "columnType": "SQLiteText", + "config": { + "columnType": "SQLiteText", + "dataType": "string", + "default": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "length": undefined, + "name": "__staticData", + "notNull": false, + "primaryKey": false, + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users___staticData_unique", + "uniqueType": undefined, + }, + "dataType": "string", + "default": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "length": undefined, + "name": "__staticData", + "notNull": false, + "primary": false, + "table": [Circular], + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users___staticData_unique", + "uniqueType": undefined, + }, + "addr": SQLiteCustomColumn { + "columnType": "SQLiteCustomColumn", + "config": { + "columnType": "SQLiteCustomColumn", + "customTypeParams": { + "dataType": [Function], + "fromDriver": [Function], + "toDriver": [Function], + }, + "dataType": "custom", + "default": undefined, + "fieldConfig": undefined, + "hasDefault": false, + "isUnique": false, + "name": "addr", + "notNull": true, + "primaryKey": false, + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users_addr_unique", + "uniqueType": undefined, + }, + "dataType": "custom", + "default": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "mapFrom": [Function], + "mapTo": [Function], + "name": "addr", + "notNull": true, + "primary": false, + "sqlName": "text", + "table": [Circular], + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users_addr_unique", + "uniqueType": undefined, + }, + "name": SQLiteText { + "columnType": "SQLiteText", + "config": { + "columnType": "SQLiteText", + "dataType": "string", + "default": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "length": undefined, + "name": "name", + "notNull": true, + "primaryKey": false, + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users_name_unique", + "uniqueType": undefined, + }, + "dataType": "string", + "default": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "length": undefined, + "name": "name", + "notNull": true, + "primary": false, + "table": [Circular], + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users_name_unique", + "uniqueType": undefined, + }, + "x": SQLiteInteger { + "autoIncrement": false, + "columnType": "SQLiteInteger", + "config": { + "autoIncrement": false, + "columnType": "SQLiteInteger", + "dataType": "number", + "default": undefined, + "hasDefault": false, + "isUnique": false, + "name": "x", + "notNull": true, + "primaryKey": false, + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users_x_unique", + "uniqueType": undefined, + }, + "dataType": "number", + "default": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "name": "x", + "notNull": true, + "primary": false, + "table": [Circular], + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users_x_unique", + "uniqueType": undefined, + }, + "y": SQLiteInteger { + "autoIncrement": false, + "columnType": "SQLiteInteger", + "config": { + "autoIncrement": false, + "columnType": "SQLiteInteger", + "dataType": "number", + "default": undefined, + "hasDefault": false, + "isUnique": false, + "name": "y", + "notNull": true, + "primaryKey": false, + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users_y_unique", + "uniqueType": undefined, + }, + "dataType": "number", + "default": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "name": "y", + "notNull": true, + "primary": false, + "table": [Circular], + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users_y_unique", + "uniqueType": undefined, + }, + Symbol(drizzle:Name): "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users", + Symbol(drizzle:OriginalName): "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users", + Symbol(drizzle:Schema): undefined, + Symbol(drizzle:Columns): { + "__dynamicData": SQLiteText { + "columnType": "SQLiteText", + "config": { + "columnType": "SQLiteText", + "dataType": "string", + "default": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "length": undefined, + "name": "__dynamicData", + "notNull": false, + "primaryKey": false, + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users___dynamicData_unique", + "uniqueType": undefined, + }, + "dataType": "string", + "default": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "length": undefined, + "name": "__dynamicData", + "notNull": false, + "primary": false, + "table": [Circular], + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users___dynamicData_unique", + "uniqueType": undefined, + }, + "__encodedLengths": SQLiteText { + "columnType": "SQLiteText", + "config": { + "columnType": "SQLiteText", + "dataType": "string", + "default": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "length": undefined, + "name": "__encodedLengths", + "notNull": false, + "primaryKey": false, + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users___encodedLengths_unique", + "uniqueType": undefined, + }, + "dataType": "string", + "default": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "length": undefined, + "name": "__encodedLengths", + "notNull": false, + "primary": false, + "table": [Circular], + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users___encodedLengths_unique", + "uniqueType": undefined, + }, + "__isDeleted": SQLiteBoolean { + "autoIncrement": false, + "columnType": "SQLiteBoolean", + "config": { + "autoIncrement": false, + "columnType": "SQLiteBoolean", + "dataType": "boolean", + "default": undefined, + "hasDefault": false, + "isUnique": false, + "mode": "boolean", + "name": "__isDeleted", + "notNull": true, + "primaryKey": false, + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users___isDeleted_unique", + "uniqueType": undefined, + }, + "dataType": "boolean", + "default": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "mode": "boolean", + "name": "__isDeleted", + "notNull": true, + "primary": false, + "table": [Circular], + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users___isDeleted_unique", + "uniqueType": undefined, + }, + "__key": SQLiteText { + "columnType": "SQLiteText", + "config": { + "columnType": "SQLiteText", + "dataType": "string", + "default": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "length": undefined, + "name": "__key", + "notNull": true, + "primaryKey": true, + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users___key_unique", + "uniqueType": undefined, + }, + "dataType": "string", + "default": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "length": undefined, + "name": "__key", + "notNull": true, + "primary": true, + "table": [Circular], + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users___key_unique", + "uniqueType": undefined, + }, + "__lastUpdatedBlockNumber": SQLiteBigInt { + "columnType": "SQLiteBigInt", + "config": { + "columnType": "SQLiteBigInt", + "dataType": "bigint", + "default": undefined, + "hasDefault": false, + "isUnique": false, + "name": "__lastUpdatedBlockNumber", + "notNull": true, + "primaryKey": false, + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users___lastUpdatedBlockNumber_unique", + "uniqueType": undefined, + }, + "dataType": "bigint", + "default": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "name": "__lastUpdatedBlockNumber", + "notNull": true, + "primary": false, + "table": [Circular], + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users___lastUpdatedBlockNumber_unique", + "uniqueType": undefined, + }, + "__staticData": SQLiteText { + "columnType": "SQLiteText", + "config": { + "columnType": "SQLiteText", + "dataType": "string", + "default": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "length": undefined, + "name": "__staticData", + "notNull": false, + "primaryKey": false, + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users___staticData_unique", + "uniqueType": undefined, + }, + "dataType": "string", + "default": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "length": undefined, + "name": "__staticData", + "notNull": false, + "primary": false, + "table": [Circular], + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users___staticData_unique", + "uniqueType": undefined, + }, + "addr": SQLiteCustomColumn { + "columnType": "SQLiteCustomColumn", + "config": { + "columnType": "SQLiteCustomColumn", + "customTypeParams": { + "dataType": [Function], + "fromDriver": [Function], + "toDriver": [Function], + }, + "dataType": "custom", + "default": undefined, + "fieldConfig": undefined, + "hasDefault": false, + "isUnique": false, + "name": "addr", + "notNull": true, + "primaryKey": false, + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users_addr_unique", + "uniqueType": undefined, + }, + "dataType": "custom", + "default": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "mapFrom": [Function], + "mapTo": [Function], + "name": "addr", + "notNull": true, + "primary": false, + "sqlName": "text", + "table": [Circular], + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users_addr_unique", + "uniqueType": undefined, + }, + "name": SQLiteText { + "columnType": "SQLiteText", + "config": { + "columnType": "SQLiteText", + "dataType": "string", + "default": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "length": undefined, + "name": "name", + "notNull": true, + "primaryKey": false, + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users_name_unique", + "uniqueType": undefined, + }, + "dataType": "string", + "default": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "length": undefined, + "name": "name", + "notNull": true, + "primary": false, + "table": [Circular], + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users_name_unique", + "uniqueType": undefined, + }, + "x": SQLiteInteger { + "autoIncrement": false, + "columnType": "SQLiteInteger", + "config": { + "autoIncrement": false, + "columnType": "SQLiteInteger", + "dataType": "number", + "default": undefined, + "hasDefault": false, + "isUnique": false, + "name": "x", + "notNull": true, + "primaryKey": false, + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users_x_unique", + "uniqueType": undefined, + }, + "dataType": "number", + "default": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "name": "x", + "notNull": true, + "primary": false, + "table": [Circular], + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users_x_unique", + "uniqueType": undefined, + }, + "y": SQLiteInteger { + "autoIncrement": false, + "columnType": "SQLiteInteger", + "config": { + "autoIncrement": false, + "columnType": "SQLiteInteger", + "dataType": "number", + "default": undefined, + "hasDefault": false, + "isUnique": false, + "name": "y", + "notNull": true, + "primaryKey": false, + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users_y_unique", + "uniqueType": undefined, + }, + "dataType": "number", + "default": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "name": "y", + "notNull": true, + "primary": false, + "table": [Circular], + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users_y_unique", + "uniqueType": undefined, + }, + }, + Symbol(drizzle:BaseName): "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users", + Symbol(drizzle:IsAlias): false, + Symbol(drizzle:ExtraConfigBuilder): undefined, + Symbol(drizzle:IsDrizzleTable): true, + Symbol(drizzle:SQLiteInlineForeignKeys): [], + } + `); + }); + + it("can create a singleton table", async () => { + const table = buildTable({ + address: "0xffffffffffffffffffffffffffffffffffffffff", + namespace: "test", + name: "users", + keySchema: {}, + valueSchema: { addrs: "address[]" }, + }); + + expect(table).toMatchInlineSnapshot(` + SQLiteTable { + "__dynamicData": SQLiteText { + "columnType": "SQLiteText", + "config": { + "columnType": "SQLiteText", + "dataType": "string", + "default": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "length": undefined, + "name": "__dynamicData", + "notNull": false, + "primaryKey": false, + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users___dynamicData_unique", + "uniqueType": undefined, + }, + "dataType": "string", + "default": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "length": undefined, + "name": "__dynamicData", + "notNull": false, + "primary": false, + "table": [Circular], + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users___dynamicData_unique", + "uniqueType": undefined, + }, + "__encodedLengths": SQLiteText { + "columnType": "SQLiteText", + "config": { + "columnType": "SQLiteText", + "dataType": "string", + "default": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "length": undefined, + "name": "__encodedLengths", + "notNull": false, + "primaryKey": false, + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users___encodedLengths_unique", + "uniqueType": undefined, + }, + "dataType": "string", + "default": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "length": undefined, + "name": "__encodedLengths", + "notNull": false, + "primary": false, + "table": [Circular], + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users___encodedLengths_unique", + "uniqueType": undefined, + }, + "__isDeleted": SQLiteBoolean { + "autoIncrement": false, + "columnType": "SQLiteBoolean", + "config": { + "autoIncrement": false, + "columnType": "SQLiteBoolean", + "dataType": "boolean", + "default": undefined, + "hasDefault": false, + "isUnique": false, + "mode": "boolean", + "name": "__isDeleted", + "notNull": true, + "primaryKey": false, + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users___isDeleted_unique", + "uniqueType": undefined, + }, + "dataType": "boolean", + "default": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "mode": "boolean", + "name": "__isDeleted", + "notNull": true, + "primary": false, + "table": [Circular], + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users___isDeleted_unique", + "uniqueType": undefined, + }, + "__key": SQLiteText { + "columnType": "SQLiteText", + "config": { + "columnType": "SQLiteText", + "dataType": "string", + "default": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "length": undefined, + "name": "__key", + "notNull": true, + "primaryKey": true, + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users___key_unique", + "uniqueType": undefined, + }, + "dataType": "string", + "default": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "length": undefined, + "name": "__key", + "notNull": true, + "primary": true, + "table": [Circular], + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users___key_unique", + "uniqueType": undefined, + }, + "__lastUpdatedBlockNumber": SQLiteBigInt { + "columnType": "SQLiteBigInt", + "config": { + "columnType": "SQLiteBigInt", + "dataType": "bigint", + "default": undefined, + "hasDefault": false, + "isUnique": false, + "name": "__lastUpdatedBlockNumber", + "notNull": true, + "primaryKey": false, + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users___lastUpdatedBlockNumber_unique", + "uniqueType": undefined, + }, + "dataType": "bigint", + "default": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "name": "__lastUpdatedBlockNumber", + "notNull": true, + "primary": false, + "table": [Circular], + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users___lastUpdatedBlockNumber_unique", + "uniqueType": undefined, + }, + "__staticData": SQLiteText { + "columnType": "SQLiteText", + "config": { + "columnType": "SQLiteText", + "dataType": "string", + "default": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "length": undefined, + "name": "__staticData", + "notNull": false, + "primaryKey": false, + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users___staticData_unique", + "uniqueType": undefined, + }, + "dataType": "string", + "default": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "length": undefined, + "name": "__staticData", + "notNull": false, + "primary": false, + "table": [Circular], + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users___staticData_unique", + "uniqueType": undefined, + }, + "addrs": SQLiteCustomColumn { + "columnType": "SQLiteCustomColumn", + "config": { + "columnType": "SQLiteCustomColumn", + "customTypeParams": { + "dataType": [Function], + "fromDriver": [Function], + "toDriver": [Function], + }, + "dataType": "custom", + "default": undefined, + "fieldConfig": undefined, + "hasDefault": false, + "isUnique": false, + "name": "addrs", + "notNull": true, + "primaryKey": false, + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users_addrs_unique", + "uniqueType": undefined, + }, + "dataType": "custom", + "default": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "mapFrom": [Function], + "mapTo": [Function], + "name": "addrs", + "notNull": true, + "primary": false, + "sqlName": "text", + "table": [Circular], + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users_addrs_unique", + "uniqueType": undefined, + }, + Symbol(drizzle:Name): "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users", + Symbol(drizzle:OriginalName): "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users", + Symbol(drizzle:Schema): undefined, + Symbol(drizzle:Columns): { + "__dynamicData": SQLiteText { + "columnType": "SQLiteText", + "config": { + "columnType": "SQLiteText", + "dataType": "string", + "default": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "length": undefined, + "name": "__dynamicData", + "notNull": false, + "primaryKey": false, + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users___dynamicData_unique", + "uniqueType": undefined, + }, + "dataType": "string", + "default": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "length": undefined, + "name": "__dynamicData", + "notNull": false, + "primary": false, + "table": [Circular], + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users___dynamicData_unique", + "uniqueType": undefined, + }, + "__encodedLengths": SQLiteText { + "columnType": "SQLiteText", + "config": { + "columnType": "SQLiteText", + "dataType": "string", + "default": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "length": undefined, + "name": "__encodedLengths", + "notNull": false, + "primaryKey": false, + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users___encodedLengths_unique", + "uniqueType": undefined, + }, + "dataType": "string", + "default": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "length": undefined, + "name": "__encodedLengths", + "notNull": false, + "primary": false, + "table": [Circular], + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users___encodedLengths_unique", + "uniqueType": undefined, + }, + "__isDeleted": SQLiteBoolean { + "autoIncrement": false, + "columnType": "SQLiteBoolean", + "config": { + "autoIncrement": false, + "columnType": "SQLiteBoolean", + "dataType": "boolean", + "default": undefined, + "hasDefault": false, + "isUnique": false, + "mode": "boolean", + "name": "__isDeleted", + "notNull": true, + "primaryKey": false, + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users___isDeleted_unique", + "uniqueType": undefined, + }, + "dataType": "boolean", + "default": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "mode": "boolean", + "name": "__isDeleted", + "notNull": true, + "primary": false, + "table": [Circular], + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users___isDeleted_unique", + "uniqueType": undefined, + }, + "__key": SQLiteText { + "columnType": "SQLiteText", + "config": { + "columnType": "SQLiteText", + "dataType": "string", + "default": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "length": undefined, + "name": "__key", + "notNull": true, + "primaryKey": true, + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users___key_unique", + "uniqueType": undefined, + }, + "dataType": "string", + "default": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "length": undefined, + "name": "__key", + "notNull": true, + "primary": true, + "table": [Circular], + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users___key_unique", + "uniqueType": undefined, + }, + "__lastUpdatedBlockNumber": SQLiteBigInt { + "columnType": "SQLiteBigInt", + "config": { + "columnType": "SQLiteBigInt", + "dataType": "bigint", + "default": undefined, + "hasDefault": false, + "isUnique": false, + "name": "__lastUpdatedBlockNumber", + "notNull": true, + "primaryKey": false, + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users___lastUpdatedBlockNumber_unique", + "uniqueType": undefined, + }, + "dataType": "bigint", + "default": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "name": "__lastUpdatedBlockNumber", + "notNull": true, + "primary": false, + "table": [Circular], + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users___lastUpdatedBlockNumber_unique", + "uniqueType": undefined, + }, + "__staticData": SQLiteText { + "columnType": "SQLiteText", + "config": { + "columnType": "SQLiteText", + "dataType": "string", + "default": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "length": undefined, + "name": "__staticData", + "notNull": false, + "primaryKey": false, + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users___staticData_unique", + "uniqueType": undefined, + }, + "dataType": "string", + "default": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "length": undefined, + "name": "__staticData", + "notNull": false, + "primary": false, + "table": [Circular], + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users___staticData_unique", + "uniqueType": undefined, + }, + "addrs": SQLiteCustomColumn { + "columnType": "SQLiteCustomColumn", + "config": { + "columnType": "SQLiteCustomColumn", + "customTypeParams": { + "dataType": [Function], + "fromDriver": [Function], + "toDriver": [Function], + }, + "dataType": "custom", + "default": undefined, + "fieldConfig": undefined, + "hasDefault": false, + "isUnique": false, + "name": "addrs", + "notNull": true, + "primaryKey": false, + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users_addrs_unique", + "uniqueType": undefined, + }, + "dataType": "custom", + "default": undefined, + "defaultFn": undefined, + "enumValues": undefined, + "hasDefault": false, + "isUnique": false, + "mapFrom": [Function], + "mapTo": [Function], + "name": "addrs", + "notNull": true, + "primary": false, + "sqlName": "text", + "table": [Circular], + "uniqueName": "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users_addrs_unique", + "uniqueType": undefined, + }, + }, + Symbol(drizzle:BaseName): "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users", + Symbol(drizzle:IsAlias): false, + Symbol(drizzle:ExtraConfigBuilder): undefined, + Symbol(drizzle:IsDrizzleTable): true, + Symbol(drizzle:SQLiteInlineForeignKeys): [], + } + `); + }); +}); diff --git a/packages/store-sync/src/sqlite/createSqliteTable.ts b/packages/store-sync/src/sqlite/buildTable.ts similarity index 70% rename from packages/store-sync/src/sqlite/createSqliteTable.ts rename to packages/store-sync/src/sqlite/buildTable.ts index e8e8667b7d..2b9b0efd5c 100644 --- a/packages/store-sync/src/sqlite/createSqliteTable.ts +++ b/packages/store-sync/src/sqlite/buildTable.ts @@ -1,17 +1,21 @@ -import { AnySQLiteColumnBuilder, SQLiteTableWithColumns, sqliteTable } from "drizzle-orm/sqlite-core"; -import { buildSqliteColumn } from "./buildSqliteColumn"; +import { SQLiteColumnBuilderBase, SQLiteTableWithColumns, sqliteTable } from "drizzle-orm/sqlite-core"; +import { buildColumn } from "./buildColumn"; import { Address } from "viem"; import { getTableName } from "./getTableName"; import { KeySchema, ValueSchema } from "@latticexyz/protocol-parser"; export const metaColumns = { - __key: buildSqliteColumn("__key", "bytes").notNull().primaryKey(), - __lastUpdatedBlockNumber: buildSqliteColumn("__lastUpdatedBlockNumber", "uint256").notNull(), + __key: buildColumn("__key", "bytes").primaryKey(), + __staticData: buildColumn("__staticData", "bytes"), + __encodedLengths: buildColumn("__encodedLengths", "bytes"), + __dynamicData: buildColumn("__dynamicData", "bytes"), + __lastUpdatedBlockNumber: buildColumn("__lastUpdatedBlockNumber", "uint256").notNull(), // TODO: last updated block hash? - __isDeleted: buildSqliteColumn("__isDeleted", "bool").notNull(), -} as const satisfies Record; + __isDeleted: buildColumn("__isDeleted", "bool").notNull(), +} as const satisfies Record; type SQLiteTableFromSchema = SQLiteTableWithColumns<{ + dialect: "sqlite"; name: string; schema: string | undefined; columns: { @@ -39,7 +43,7 @@ type CreateSqliteTableResult; -export function createSqliteTable({ +export function buildTable({ address, namespace, name, @@ -49,11 +53,11 @@ export function createSqliteTable [name, buildSqliteColumn(name, type).notNull()]) + Object.entries(keySchema).map(([name, type]) => [name, buildColumn(name, type).notNull()]) ); const valueColumns = Object.fromEntries( - Object.entries(valueSchema).map(([name, type]) => [name, buildSqliteColumn(name, type).notNull()]) + Object.entries(valueSchema).map(([name, type]) => [name, buildColumn(name, type).notNull()]) ); // TODO: unique constraint on key columns? diff --git a/packages/store-sync/src/sqlite/columnTypes.ts b/packages/store-sync/src/sqlite/columnTypes.ts index dc23a26f5c..3dd4c07e9f 100644 --- a/packages/store-sync/src/sqlite/columnTypes.ts +++ b/packages/store-sync/src/sqlite/columnTypes.ts @@ -1,9 +1,9 @@ -import { customType, SQLiteCustomColumnBuilder } from "drizzle-orm/sqlite-core"; -import { ColumnBuilderBaseConfig } from "drizzle-orm"; +import { customType } from "drizzle-orm/sqlite-core"; import superjson from "superjson"; import { Address, getAddress } from "viem"; -export const json = (name: string): SQLiteCustomColumnBuilder => +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +export const json = (name: string) => customType<{ data: TData; driverData: string }>({ dataType() { return "text"; @@ -16,7 +16,8 @@ export const json = (name: string): SQLiteCustomColumnBuilder => +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +export const address = (name: string) => customType<{ data: Address; driverData: string }>({ dataType() { return "text"; diff --git a/packages/store-sync/src/sqlite/createSqliteTable.test.ts b/packages/store-sync/src/sqlite/createSqliteTable.test.ts deleted file mode 100644 index 756055c09d..0000000000 --- a/packages/store-sync/src/sqlite/createSqliteTable.test.ts +++ /dev/null @@ -1,446 +0,0 @@ -import { describe, it, expect } from "vitest"; -import { createSqliteTable } from "./createSqliteTable"; - -describe("createSqliteTable", () => { - it("should create table from schema", async () => { - const table = createSqliteTable({ - address: "0xffffffffffffffffffffffffffffffffffffffff", - namespace: "test", - name: "users", - keySchema: { x: "uint32", y: "uint32" }, - valueSchema: { name: "string", addr: "address" }, - }); - - expect(table).toMatchInlineSnapshot(` - SQLiteTable { - "__isDeleted": SQLiteBoolean { - "autoIncrement": false, - "config": { - "autoIncrement": false, - "default": undefined, - "mode": "boolean", - "name": "__isDeleted", - "notNull": true, - "primaryKey": false, - }, - "default": undefined, - "hasDefault": undefined, - "mode": "boolean", - "name": "__isDeleted", - "notNull": true, - "primary": false, - "table": [Circular], - }, - "__key": SQLiteText { - "config": { - "default": undefined, - "enumValues": [], - "length": undefined, - "name": "__key", - "notNull": true, - "primaryKey": true, - }, - "default": undefined, - "enumValues": [], - "hasDefault": undefined, - "length": undefined, - "name": "__key", - "notNull": true, - "primary": true, - "table": [Circular], - }, - "__lastUpdatedBlockNumber": SQLiteBigInt { - "config": { - "default": undefined, - "name": "__lastUpdatedBlockNumber", - "notNull": true, - "primaryKey": false, - }, - "default": undefined, - "hasDefault": undefined, - "name": "__lastUpdatedBlockNumber", - "notNull": true, - "primary": false, - "table": [Circular], - }, - "addr": SQLiteCustomColumn { - "config": { - "customTypeParams": { - "dataType": [Function], - "fromDriver": [Function], - "toDriver": [Function], - }, - "default": undefined, - "fieldConfig": undefined, - "name": "addr", - "notNull": true, - "primaryKey": false, - }, - "default": undefined, - "hasDefault": undefined, - "mapFrom": [Function], - "mapTo": [Function], - "name": "addr", - "notNull": true, - "primary": false, - "sqlName": "text", - "table": [Circular], - }, - "name": SQLiteText { - "config": { - "default": undefined, - "enumValues": [], - "length": undefined, - "name": "name", - "notNull": true, - "primaryKey": false, - }, - "default": undefined, - "enumValues": [], - "hasDefault": undefined, - "length": undefined, - "name": "name", - "notNull": true, - "primary": false, - "table": [Circular], - }, - "x": SQLiteInteger { - "autoIncrement": false, - "config": { - "autoIncrement": false, - "default": undefined, - "name": "x", - "notNull": true, - "primaryKey": false, - }, - "default": undefined, - "hasDefault": undefined, - "name": "x", - "notNull": true, - "primary": false, - "table": [Circular], - }, - "y": SQLiteInteger { - "autoIncrement": false, - "config": { - "autoIncrement": false, - "default": undefined, - "name": "y", - "notNull": true, - "primaryKey": false, - }, - "default": undefined, - "hasDefault": undefined, - "name": "y", - "notNull": true, - "primary": false, - "table": [Circular], - }, - Symbol(drizzle:Name): "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users", - Symbol(drizzle:OriginalName): "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users", - Symbol(drizzle:Schema): undefined, - Symbol(drizzle:Columns): { - "__isDeleted": SQLiteBoolean { - "autoIncrement": false, - "config": { - "autoIncrement": false, - "default": undefined, - "mode": "boolean", - "name": "__isDeleted", - "notNull": true, - "primaryKey": false, - }, - "default": undefined, - "hasDefault": undefined, - "mode": "boolean", - "name": "__isDeleted", - "notNull": true, - "primary": false, - "table": [Circular], - }, - "__key": SQLiteText { - "config": { - "default": undefined, - "enumValues": [], - "length": undefined, - "name": "__key", - "notNull": true, - "primaryKey": true, - }, - "default": undefined, - "enumValues": [], - "hasDefault": undefined, - "length": undefined, - "name": "__key", - "notNull": true, - "primary": true, - "table": [Circular], - }, - "__lastUpdatedBlockNumber": SQLiteBigInt { - "config": { - "default": undefined, - "name": "__lastUpdatedBlockNumber", - "notNull": true, - "primaryKey": false, - }, - "default": undefined, - "hasDefault": undefined, - "name": "__lastUpdatedBlockNumber", - "notNull": true, - "primary": false, - "table": [Circular], - }, - "addr": SQLiteCustomColumn { - "config": { - "customTypeParams": { - "dataType": [Function], - "fromDriver": [Function], - "toDriver": [Function], - }, - "default": undefined, - "fieldConfig": undefined, - "name": "addr", - "notNull": true, - "primaryKey": false, - }, - "default": undefined, - "hasDefault": undefined, - "mapFrom": [Function], - "mapTo": [Function], - "name": "addr", - "notNull": true, - "primary": false, - "sqlName": "text", - "table": [Circular], - }, - "name": SQLiteText { - "config": { - "default": undefined, - "enumValues": [], - "length": undefined, - "name": "name", - "notNull": true, - "primaryKey": false, - }, - "default": undefined, - "enumValues": [], - "hasDefault": undefined, - "length": undefined, - "name": "name", - "notNull": true, - "primary": false, - "table": [Circular], - }, - "x": SQLiteInteger { - "autoIncrement": false, - "config": { - "autoIncrement": false, - "default": undefined, - "name": "x", - "notNull": true, - "primaryKey": false, - }, - "default": undefined, - "hasDefault": undefined, - "name": "x", - "notNull": true, - "primary": false, - "table": [Circular], - }, - "y": SQLiteInteger { - "autoIncrement": false, - "config": { - "autoIncrement": false, - "default": undefined, - "name": "y", - "notNull": true, - "primaryKey": false, - }, - "default": undefined, - "hasDefault": undefined, - "name": "y", - "notNull": true, - "primary": false, - "table": [Circular], - }, - }, - Symbol(drizzle:BaseName): "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users", - Symbol(drizzle:IsAlias): false, - Symbol(drizzle:ExtraConfigBuilder): undefined, - Symbol(drizzle:IsDrizzleTable): true, - Symbol(drizzle:SQLiteInlineForeignKeys): [], - } - `); - }); - - it("can create a singleton table", async () => { - const table = createSqliteTable({ - address: "0xffffffffffffffffffffffffffffffffffffffff", - namespace: "test", - name: "users", - keySchema: {}, - valueSchema: { addrs: "address[]" }, - }); - - expect(table).toMatchInlineSnapshot(` - SQLiteTable { - "__isDeleted": SQLiteBoolean { - "autoIncrement": false, - "config": { - "autoIncrement": false, - "default": undefined, - "mode": "boolean", - "name": "__isDeleted", - "notNull": true, - "primaryKey": false, - }, - "default": undefined, - "hasDefault": undefined, - "mode": "boolean", - "name": "__isDeleted", - "notNull": true, - "primary": false, - "table": [Circular], - }, - "__key": SQLiteText { - "config": { - "default": undefined, - "enumValues": [], - "length": undefined, - "name": "__key", - "notNull": true, - "primaryKey": true, - }, - "default": undefined, - "enumValues": [], - "hasDefault": undefined, - "length": undefined, - "name": "__key", - "notNull": true, - "primary": true, - "table": [Circular], - }, - "__lastUpdatedBlockNumber": SQLiteBigInt { - "config": { - "default": undefined, - "name": "__lastUpdatedBlockNumber", - "notNull": true, - "primaryKey": false, - }, - "default": undefined, - "hasDefault": undefined, - "name": "__lastUpdatedBlockNumber", - "notNull": true, - "primary": false, - "table": [Circular], - }, - "addrs": SQLiteCustomColumn { - "config": { - "customTypeParams": { - "dataType": [Function], - "fromDriver": [Function], - "toDriver": [Function], - }, - "default": undefined, - "fieldConfig": undefined, - "name": "addrs", - "notNull": true, - "primaryKey": false, - }, - "default": undefined, - "hasDefault": undefined, - "mapFrom": [Function], - "mapTo": [Function], - "name": "addrs", - "notNull": true, - "primary": false, - "sqlName": "text", - "table": [Circular], - }, - Symbol(drizzle:Name): "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users", - Symbol(drizzle:OriginalName): "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users", - Symbol(drizzle:Schema): undefined, - Symbol(drizzle:Columns): { - "__isDeleted": SQLiteBoolean { - "autoIncrement": false, - "config": { - "autoIncrement": false, - "default": undefined, - "mode": "boolean", - "name": "__isDeleted", - "notNull": true, - "primaryKey": false, - }, - "default": undefined, - "hasDefault": undefined, - "mode": "boolean", - "name": "__isDeleted", - "notNull": true, - "primary": false, - "table": [Circular], - }, - "__key": SQLiteText { - "config": { - "default": undefined, - "enumValues": [], - "length": undefined, - "name": "__key", - "notNull": true, - "primaryKey": true, - }, - "default": undefined, - "enumValues": [], - "hasDefault": undefined, - "length": undefined, - "name": "__key", - "notNull": true, - "primary": true, - "table": [Circular], - }, - "__lastUpdatedBlockNumber": SQLiteBigInt { - "config": { - "default": undefined, - "name": "__lastUpdatedBlockNumber", - "notNull": true, - "primaryKey": false, - }, - "default": undefined, - "hasDefault": undefined, - "name": "__lastUpdatedBlockNumber", - "notNull": true, - "primary": false, - "table": [Circular], - }, - "addrs": SQLiteCustomColumn { - "config": { - "customTypeParams": { - "dataType": [Function], - "fromDriver": [Function], - "toDriver": [Function], - }, - "default": undefined, - "fieldConfig": undefined, - "name": "addrs", - "notNull": true, - "primaryKey": false, - }, - "default": undefined, - "hasDefault": undefined, - "mapFrom": [Function], - "mapTo": [Function], - "name": "addrs", - "notNull": true, - "primary": false, - "sqlName": "text", - "table": [Circular], - }, - }, - Symbol(drizzle:BaseName): "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF__test__users", - Symbol(drizzle:IsAlias): false, - Symbol(drizzle:ExtraConfigBuilder): undefined, - Symbol(drizzle:IsDrizzleTable): true, - Symbol(drizzle:SQLiteInlineForeignKeys): [], - } - `); - }); -}); diff --git a/packages/store-sync/src/sqlite/getTables.ts b/packages/store-sync/src/sqlite/getTables.ts index 6d1a4f4d03..9a99afacbf 100644 --- a/packages/store-sync/src/sqlite/getTables.ts +++ b/packages/store-sync/src/sqlite/getTables.ts @@ -1,9 +1,9 @@ import { BaseSQLiteDatabase } from "drizzle-orm/sqlite-core"; import { inArray } from "drizzle-orm"; import { Table } from "../common"; -import { TableId } from "@latticexyz/common/deprecated"; import { getTableName } from "./getTableName"; import { mudStoreTables } from "./internalTables"; +import { tableIdToHex } from "@latticexyz/common"; export function getTables( db: BaseSQLiteDatabase<"sync", void>, @@ -19,7 +19,7 @@ export function getTables( .all(); return tables.map((table) => { - const tableId = new TableId(table.namespace, table.name).toHex(); + const tableId = tableIdToHex(table.namespace, table.name); return { id: getTableName(table.address, table.namespace, table.name), address: table.address, diff --git a/packages/store-sync/src/sqlite/index.ts b/packages/store-sync/src/sqlite/index.ts index bcfd3948ad..8e6b2acd2e 100644 --- a/packages/store-sync/src/sqlite/index.ts +++ b/packages/store-sync/src/sqlite/index.ts @@ -1,4 +1,4 @@ -export * from "./createSqliteTable"; +export * from "./buildTable"; export * from "./getTables"; export * from "./internalTables"; export * from "./schemaVersion"; diff --git a/packages/store-sync/src/sqlite/sqliteStorage.test.ts b/packages/store-sync/src/sqlite/sqliteStorage.test.ts index 414c104b25..1c8845f37c 100644 --- a/packages/store-sync/src/sqlite/sqliteStorage.test.ts +++ b/packages/store-sync/src/sqlite/sqliteStorage.test.ts @@ -3,13 +3,29 @@ import { sqliteStorage } from "./sqliteStorage"; import { getTables } from "./getTables"; import { chainState, mudStoreTables } from "./internalTables"; import { BaseSQLiteDatabase } from "drizzle-orm/sqlite-core"; -import { createSqliteTable } from "./createSqliteTable"; +import { buildTable } from "./buildTable"; import initSqlJs from "sql.js"; import { drizzle } from "drizzle-orm/sql-js"; -import { createPublicClient, http } from "viem"; +import { Hex, RpcLog, createPublicClient, decodeEventLog, formatLog, http } from "viem"; import { foundry } from "viem/chains"; -import { blockLogsToStorage } from "../blockLogsToStorage"; +import worldRpcLogs from "../../../../test-data/world-logs.json"; +import { storeEventsAbi } from "@latticexyz/store"; import { StoreEventsLog } from "../common"; +import { groupLogsByBlockNumber } from "@latticexyz/block-logs-stream"; +import { eq } from "drizzle-orm"; + +// TODO: make test-data a proper package and export this +const blocks = groupLogsByBlockNumber( + worldRpcLogs.map((log) => { + const { eventName, args } = decodeEventLog({ + abi: storeEventsAbi, + data: log.data as Hex, + topics: log.topics as [Hex, ...Hex[]], + strict: true, + }); + return formatLog(log as any as RpcLog, { args, eventName: eventName as string }) as StoreEventsLog; + }) +); describe("sqliteStorage", async () => { const SqlJs = await initSqlJs(); @@ -39,104 +55,74 @@ describe("sqliteStorage", async () => { expect(db.select().from(chainState).all()).toMatchInlineSnapshot("[]"); expect(db.select().from(mudStoreTables).all()).toMatchInlineSnapshot("[]"); - await blockLogsToStorage(storageAdapter)({ - blockNumber: 5448n, - logs: [ - { - address: "0x5fbdb2315678afecb367f032d93f642f64180aa3", - topics: ["0xd01f9f1368f831528fc9fe6442366b2b7d957fbfff3bcf7c24d9ab5fe51f8c46"], - data: "0x000000000000000000000000000000005265736f7572636554797065000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000496e76656e746f72790000000000000000000000000000000000000000000000000000000000000000000000000000010200000000000000000000000000000000000000000000000000000000000000", - blockHash: "0x4ad3752c86f900332e0d2d8903480e7206747d233586574d16f006eebdb5138b", - blockNumber: 2n, - transactionHash: "0xaa54bf18053cce5d4d2906538a60cb1d9958cc3c10c34b5f9fdc92fe6a6abab4", - transactionIndex: 16, - logIndex: 54, - removed: false, - args: { - tableId: "0x000000000000000000000000000000005265736f757263655479706500000000", - keyTuple: ["0x00000000000000000000000000000000496e76656e746f727900000000000000"], - schemaIndex: 0, - data: "0x02", - }, - eventName: "StoreSetField", - }, - { - address: "0x5fbdb2315678afecb367f032d93f642f64180aa3", - topics: ["0x912af873e852235aae78a1d25ae9bb28b616a67c36898c53a14fd8184504ee32"], - data: "0x6d756473746f726500000000000000005461626c657300000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000496e76656e746f72790000000000000000000000000000000000000000000000000000000000000000000000000002800004010004000000000000000000000000000000000000000000000000000000001c030061030300000000000000000000000000000000000000000000000000000401000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000001600000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000056f776e657200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046974656d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b6974656d56617269616e740000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000006616d6f756e740000000000000000000000000000000000000000000000000000", - blockHash: "0x4ad3752c86f900332e0d2d8903480e7206747d233586574d16f006eebdb5138b", - blockNumber: 2n, - transactionHash: "0xaa54bf18053cce5d4d2906538a60cb1d9958cc3c10c34b5f9fdc92fe6a6abab4", - transactionIndex: 16, - logIndex: 55, - removed: false, - args: { - tableId: "0x6d756473746f726500000000000000005461626c657300000000000000000000", - keyTuple: ["0x00000000000000000000000000000000496e76656e746f727900000000000000"], - data: "0x0004010004000000000000000000000000000000000000000000000000000000001c030061030300000000000000000000000000000000000000000000000000000401000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000001600000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000056f776e657200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046974656d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b6974656d56617269616e740000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000006616d6f756e740000000000000000000000000000000000000000000000000000", - }, - eventName: "StoreSetRecord", - }, - ] satisfies StoreEventsLog[], - }); + for (const block of blocks) { + await storageAdapter(block); + } expect(db.select().from(chainState).all()).toMatchInlineSnapshot(` [ { "chainId": 31337, "lastError": null, - "lastUpdatedBlockNumber": 5448n, + "lastUpdatedBlockNumber": 5n, "schemaVersion": 1, }, ] `); - expect(db.select().from(mudStoreTables).all()).toMatchInlineSnapshot(` + expect(db.select().from(mudStoreTables).where(eq(mudStoreTables.name, "NumberList")).all()).toMatchInlineSnapshot(` [ { "address": "0x5FbDB2315678afecb367f032d93F642f64180aa3", - "id": "0x5FbDB2315678afecb367f032d93F642f64180aa3____Inventory", - "keySchema": { - "item": "uint32", - "itemVariant": "uint32", - "owner": "address", - }, + "id": "0x5FbDB2315678afecb367f032d93F642f64180aa3____NumberList", + "keySchema": {}, "lastError": null, - "lastUpdatedBlockNumber": 5448n, - "name": "Inventory", + "lastUpdatedBlockNumber": 5n, + "name": "NumberList", "namespace": "", "schemaVersion": 1, - "tableId": "0x00000000000000000000000000000000496e76656e746f727900000000000000", + "tableId": "0x000000000000000000000000000000004e756d6265724c697374000000000000", "valueSchema": { - "amount": "uint32", + "value": "uint32[]", }, }, ] `); - const tables = getTables(db); + const tables = getTables(db).filter((table) => table.name === "NumberList"); expect(tables).toMatchInlineSnapshot(` [ { "address": "0x5FbDB2315678afecb367f032d93F642f64180aa3", - "id": "0x5FbDB2315678afecb367f032d93F642f64180aa3____Inventory", - "keySchema": { - "item": "uint32", - "itemVariant": "uint32", - "owner": "address", - }, - "lastUpdatedBlockNumber": 5448n, - "name": "Inventory", + "id": "0x5FbDB2315678afecb367f032d93F642f64180aa3____NumberList", + "keySchema": {}, + "lastUpdatedBlockNumber": 5n, + "name": "NumberList", "namespace": "", - "tableId": "0x00000000000000000000000000000000496e76656e746f727900000000000000", + "tableId": "0x000000000000000000000000000000004e756d6265724c697374000000000000", "valueSchema": { - "amount": "uint32", + "value": "uint32[]", }, }, ] `); - const sqliteTable = createSqliteTable(tables[0]); - expect(db.select().from(sqliteTable).all()).toMatchInlineSnapshot("[]"); + const sqlTable = buildTable(tables[0]); + expect(db.select().from(sqlTable).all()).toMatchInlineSnapshot(` + [ + { + "__dynamicData": "0x000001a400000045", + "__encodedLengths": "0x0000000000000000000000000000000000000000000000000800000000000008", + "__isDeleted": false, + "__key": "0x", + "__lastUpdatedBlockNumber": 5n, + "__staticData": null, + "value": [ + 420, + 69, + ], + }, + ] + `); }); }); diff --git a/packages/store-sync/src/sqlite/sqliteStorage.ts b/packages/store-sync/src/sqlite/sqliteStorage.ts index fb2073b11c..08ca78f547 100644 --- a/packages/store-sync/src/sqlite/sqliteStorage.ts +++ b/packages/store-sync/src/sqlite/sqliteStorage.ts @@ -1,10 +1,8 @@ -import { PublicClient, concatHex, encodeAbiParameters, getAddress } from "viem"; +import { Hex, PublicClient, concatHex, getAddress } from "viem"; import { BaseSQLiteDatabase } from "drizzle-orm/sqlite-core"; import { and, eq, sql } from "drizzle-orm"; import { sqliteTableToSql } from "./sqliteTableToSql"; -import { createSqliteTable } from "./createSqliteTable"; -import { schemaToDefaults } from "../schemaToDefaults"; -import { TableId } from "@latticexyz/common/deprecated"; +import { buildTable } from "./buildTable"; import { StoreConfig } from "@latticexyz/store"; import { debug } from "./debug"; import { getTableName } from "./getTableName"; @@ -12,6 +10,12 @@ import { chainState, mudStoreTables } from "./internalTables"; import { getTables } from "./getTables"; import { schemaVersion } from "./schemaVersion"; import { StorageAdapter } from "../common"; +import { isTableRegistrationLog } from "../isTableRegistrationLog"; +import { logToTable } from "../logToTable"; +import { hexToTableId, spliceHex, tableIdToHex } from "@latticexyz/common"; +import { decodeKey, decodeValueArgs } from "@latticexyz/protocol-parser"; + +// TODO: upgrade drizzle and use async sqlite interface for consistency export async function sqliteStorage({ database, @@ -20,170 +24,230 @@ export async function sqliteStorage({ database: BaseSQLiteDatabase<"sync", void>; publicClient: PublicClient; config?: TConfig; -}): Promise> { +}): Promise { const chainId = publicClient.chain?.id ?? (await publicClient.getChainId()); // TODO: should these run lazily before first `registerTables`? database.run(sql.raw(sqliteTableToSql(chainState))); database.run(sql.raw(sqliteTableToSql(mudStoreTables))); - return { - async registerTables({ blockNumber, tables }) { - await database.transaction(async (tx) => { - for (const table of tables) { - debug(`creating table ${table.namespace}:${table.name} for world ${chainId}:${table.address}`); + return async function sqliteStorageAdapter({ blockNumber, logs }) { + // Find table registration logs and create new tables + const newTables = logs.filter(isTableRegistrationLog).map(logToTable); + await database.transaction(async (tx) => { + for (const table of newTables) { + debug(`creating table ${table.namespace}:${table.name} for world ${chainId}:${table.address}`); + + const sqliteTable = buildTable({ + address: table.address, + namespace: table.namespace, + name: table.name, + keySchema: table.keySchema, + valueSchema: table.valueSchema, + }); - const sqliteTable = createSqliteTable({ + tx.run(sql.raw(sqliteTableToSql(sqliteTable))); + + tx.insert(mudStoreTables) + .values({ + schemaVersion, + id: getTableName(table.address, table.namespace, table.name), address: table.address, + tableId: tableIdToHex(table.namespace, table.name), namespace: table.namespace, name: table.name, keySchema: table.keySchema, valueSchema: table.valueSchema, - }); - - tx.run(sql.raw(sqliteTableToSql(sqliteTable))); + lastUpdatedBlockNumber: blockNumber, + }) + .onConflictDoNothing() + .run(); + } + }); - tx.insert(mudStoreTables) - .values({ - schemaVersion, - id: getTableName(table.address, table.namespace, table.name), - address: table.address, - tableId: new TableId(table.namespace, table.name).toHex(), - namespace: table.namespace, - name: table.name, - keySchema: table.keySchema, - valueSchema: table.valueSchema, - lastUpdatedBlockNumber: blockNumber, + const tables = getTables( + database, + Array.from( + new Set( + logs.map((log) => + JSON.stringify({ + address: getAddress(log.address), + ...hexToTableId(log.args.tableId), }) - .onConflictDoNothing() - .run(); - } - }); - }, - async getTables({ tables }) { - // TODO: fetch any missing schemas from RPC - // TODO: cache schemas in memory? - return getTables(database, tables); - }, - async storeOperations({ blockNumber, operations }) { - // This is currently parallelized per world (each world has its own database). - // This may need to change if we decide to put multiple worlds into one DB (e.g. a namespace per world, but all under one DB). - // If so, we'll probably want to wrap the entire block worth of operations in a transaction. + ) + ) + ).map((json) => JSON.parse(json)) + ); - const tables = getTables( - database, - Array.from( - new Set( - operations.map((operation) => - JSON.stringify({ - address: getAddress(operation.address), - namespace: operation.namespace, - name: operation.name, - }) + await database.transaction(async (tx) => { + for (const { address, namespace, name } of tables) { + tx.update(mudStoreTables) + .set({ lastUpdatedBlockNumber: blockNumber }) + .where( + and( + eq(mudStoreTables.address, address), + eq(mudStoreTables.namespace, namespace), + eq(mudStoreTables.name, name) ) ) - ).map((json) => JSON.parse(json)) - ); + .run(); + } - await database.transaction(async (tx) => { - for (const { address, namespace, name } of tables) { - tx.update(mudStoreTables) - .set({ lastUpdatedBlockNumber: blockNumber }) - .where( - and( - eq(mudStoreTables.address, address), - eq(mudStoreTables.namespace, namespace), - eq(mudStoreTables.name, name) - ) - ) - .run(); + for (const log of logs) { + const table = tables.find( + (table) => table.address === getAddress(log.address) && table.tableId === log.args.tableId + ); + if (!table) { + const tableId = hexToTableId(log.args.tableId); + debug(`table ${tableId.namespace}:${tableId.name} not found, skipping log`, log); + continue; } - for (const operation of operations) { - const table = tables.find( - (table) => - table.address === getAddress(operation.address) && - table.namespace === operation.namespace && - table.name === operation.name - ); - if (!table) { - debug(`table ${operation.namespace}:${operation.name} not found, skipping operation`, operation); - continue; - } + const sqlTable = buildTable(table); + const uniqueKey = concatHex(log.args.keyTuple as Hex[]); + const key = decodeKey(table.keySchema, log.args.keyTuple); - const sqliteTable = createSqliteTable(table); - const key = concatHex( - Object.entries(table.keySchema).map(([keyName, type]) => - encodeAbiParameters([{ type }], [operation.key[keyName]]) - ) - ); - - if (operation.type === "SetRecord") { - debug("SetRecord", operation); - tx.insert(sqliteTable) - .values({ - __key: key, + if (log.eventName === "StoreSetRecord" || log.eventName === "StoreEphemeralRecord") { + const value = decodeValueArgs(table.valueSchema, log.args); + debug("upserting record", { + namespace: table.namespace, + name: table.name, + key, + value, + }); + tx.insert(sqlTable) + .values({ + __key: uniqueKey, + __staticData: log.args.staticData, + __encodedLengths: log.args.encodedLengths, + __dynamicData: log.args.dynamicData, + __lastUpdatedBlockNumber: blockNumber, + __isDeleted: false, + ...key, + ...value, + }) + .onConflictDoUpdate({ + target: sqlTable.__key, + set: { + __staticData: log.args.staticData, + __encodedLengths: log.args.encodedLengths, + __dynamicData: log.args.dynamicData, __lastUpdatedBlockNumber: blockNumber, __isDeleted: false, - ...operation.key, - ...operation.value, - }) - .onConflictDoUpdate({ - target: sqliteTable.__key, - set: { - __lastUpdatedBlockNumber: blockNumber, - __isDeleted: false, - ...operation.value, - }, - }) - .run(); - } else if (operation.type === "SetField") { - debug("SetField", operation); - tx.insert(sqliteTable) - .values({ - __key: key, + ...value, + }, + }) + .run(); + } else if (log.eventName === "StoreSpliceStaticData") { + // TODO: verify that this returns what we expect (doesn't error/undefined on no record) + const previousValue = (await tx.select().from(sqlTable).where(eq(sqlTable.__key, uniqueKey)).execute())[0]; + const previousStaticData = (previousValue?.__staticData as Hex) ?? "0x"; + const newStaticData = spliceHex(previousStaticData, log.args.start, log.args.deleteCount, log.args.data); + const newValue = decodeValueArgs(table.valueSchema, { + staticData: newStaticData, + encodedLengths: (previousValue?.__encodedLengths as Hex) ?? "0x", + dynamicData: (previousValue?.__dynamicData as Hex) ?? "0x", + }); + debug("upserting record via splice static", { + namespace: table.namespace, + name: table.name, + key, + previousStaticData, + newStaticData, + previousValue, + newValue, + }); + tx.insert(sqlTable) + .values({ + __key: uniqueKey, + __staticData: newStaticData, + __lastUpdatedBlockNumber: blockNumber, + __isDeleted: false, + ...key, + ...newValue, + }) + .onConflictDoUpdate({ + target: sqlTable.__key, + set: { + __staticData: newStaticData, __lastUpdatedBlockNumber: blockNumber, __isDeleted: false, - ...operation.key, - ...schemaToDefaults(table.valueSchema), - [operation.fieldName]: operation.fieldValue, - }) - .onConflictDoUpdate({ - target: sqliteTable.__key, - set: { - __lastUpdatedBlockNumber: blockNumber, - __isDeleted: false, - [operation.fieldName]: operation.fieldValue, - }, - }) - .run(); - } else if (operation.type === "DeleteRecord") { - // TODO: should we upsert so we at least have a DB record of when a thing was created/deleted within the same block? - debug("DeleteRecord", operation); - tx.update(sqliteTable) - .set({ + ...newValue, + }, + }) + .run(); + } else if (log.eventName === "StoreSpliceDynamicData") { + const previousValue = (await tx.select().from(sqlTable).where(eq(sqlTable.__key, uniqueKey)).execute())[0]; + const previousDynamicData = (previousValue?.__dynamicData as Hex) ?? "0x"; + const newDynamicData = spliceHex(previousDynamicData, log.args.start, log.args.deleteCount, log.args.data); + const newValue = decodeValueArgs(table.valueSchema, { + staticData: (previousValue?.__staticData as Hex) ?? "0x", + // TODO: handle unchanged encoded lengths + encodedLengths: log.args.encodedLengths, + dynamicData: newDynamicData, + }); + debug("upserting record via splice dynamic", { + namespace: table.namespace, + name: table.name, + key, + previousDynamicData, + newDynamicData, + previousValue, + newValue, + }); + tx.insert(sqlTable) + .values({ + __key: uniqueKey, + // TODO: handle unchanged encoded lengths + __encodedLengths: log.args.encodedLengths, + __dynamicData: newDynamicData, + __lastUpdatedBlockNumber: blockNumber, + __isDeleted: false, + ...key, + ...newValue, + }) + .onConflictDoUpdate({ + target: sqlTable.__key, + set: { + // TODO: handle unchanged encoded lengths + __encodedLengths: log.args.encodedLengths, + __dynamicData: newDynamicData, __lastUpdatedBlockNumber: blockNumber, - __isDeleted: true, - }) - .where(eq(sqliteTable.__key, key)) - .run(); - } + __isDeleted: false, + ...newValue, + }, + }) + .run(); + } else if (log.eventName === "StoreDeleteRecord") { + // TODO: should we upsert so we at least have a DB record of when a thing was created/deleted within the same block? + debug("deleting record", { + namespace: table.namespace, + name: table.name, + key, + }); + tx.update(sqlTable) + .set({ + __lastUpdatedBlockNumber: blockNumber, + __isDeleted: true, + }) + .where(eq(sqlTable.__key, uniqueKey)) + .run(); } + } - tx.insert(chainState) - .values({ - schemaVersion, - chainId, + tx.insert(chainState) + .values({ + schemaVersion, + chainId, + lastUpdatedBlockNumber: blockNumber, + }) + .onConflictDoUpdate({ + target: [chainState.schemaVersion, chainState.chainId], + set: { lastUpdatedBlockNumber: blockNumber, - }) - .onConflictDoUpdate({ - target: [chainState.schemaVersion, chainState.chainId], - set: { - lastUpdatedBlockNumber: blockNumber, - }, - }) - .run(); - }); - }, - } as StorageAdapter; + }, + }) + .run(); + }); + }; } diff --git a/packages/store-sync/src/sqlite/syncToSqlite.ts b/packages/store-sync/src/sqlite/syncToSqlite.ts index 2da2185bf9..210b9677d0 100644 --- a/packages/store-sync/src/sqlite/syncToSqlite.ts +++ b/packages/store-sync/src/sqlite/syncToSqlite.ts @@ -14,14 +14,14 @@ type SyncToSqliteOptions = SyncOption startSync?: boolean; }; -type SyncToSqliteResult = SyncResult & { +type SyncToSqliteResult = SyncResult & { stopSync: () => void; }; /** * Creates an indexer to process and store blockchain events. * - * @param {CreateIndexerOptions} options See `CreateIndexerOptions`. + * @param {SyncToSqliteOptions} options See `SyncToSqliteOptions`. * @returns A function to unsubscribe from the block stream, effectively stopping the indexer. */ export async function syncToSqlite({ @@ -34,7 +34,7 @@ export async function syncToSqlite({ indexerUrl, initialState, startSync = true, -}: SyncToSqliteOptions): Promise> { +}: SyncToSqliteOptions): Promise { const storeSync = await createStoreSync({ storageAdapter: await sqliteStorage({ database, publicClient, config }), config, @@ -46,7 +46,7 @@ export async function syncToSqlite({ initialState, }); - const sub = startSync ? storeSync.blockStorageOperations$.subscribe() : null; + const sub = startSync ? storeSync.storedBlockLogs$.subscribe() : null; const stopSync = (): void => { sub?.unsubscribe(); }; diff --git a/packages/store/gas-report.json b/packages/store/gas-report.json index 06772c450b..2aa0f2ab67 100644 --- a/packages/store/gas-report.json +++ b/packages/store/gas-report.json @@ -119,41 +119,71 @@ "name": "validate field layout", "gasUsed": 3944 }, + { + "file": "test/Gas.t.sol", + "test": "testCompareAbiEncodeVsCustom", + "name": "abi encode (static)", + "gasUsed": 133 + }, + { + "file": "test/Gas.t.sol", + "test": "testCompareAbiEncodeVsCustom", + "name": "abi encode (dynamic)", + "gasUsed": 849 + }, { "file": "test/Gas.t.sol", "test": "testCompareAbiEncodeVsCustom", "name": "abi encode", - "gasUsed": 949 + "gasUsed": 943 }, { "file": "test/Gas.t.sol", "test": "testCompareAbiEncodeVsCustom", "name": "abi decode", - "gasUsed": 1738 + "gasUsed": 1741 + }, + { + "file": "test/Gas.t.sol", + "test": "testCompareAbiEncodeVsCustom", + "name": "custom encode (static)", + "gasUsed": 191 + }, + { + "file": "test/Gas.t.sol", + "test": "testCompareAbiEncodeVsCustom", + "name": "custom encode (length)", + "gasUsed": 141 + }, + { + "file": "test/Gas.t.sol", + "test": "testCompareAbiEncodeVsCustom", + "name": "custom encode (dynamic)", + "gasUsed": 1152 }, { "file": "test/Gas.t.sol", "test": "testCompareAbiEncodeVsCustom", "name": "custom encode", - "gasUsed": 1394 + "gasUsed": 2065 }, { "file": "test/Gas.t.sol", "test": "testCompareAbiEncodeVsCustom", "name": "custom decode", - "gasUsed": 1976 + "gasUsed": 1981 }, { "file": "test/Gas.t.sol", "test": "testCompareAbiEncodeVsCustom", "name": "pass abi encoded bytes to external contract", - "gasUsed": 6550 + "gasUsed": 6562 }, { "file": "test/Gas.t.sol", "test": "testCompareAbiEncodeVsCustom", "name": "pass custom encoded bytes to external contract", - "gasUsed": 1444 + "gasUsed": 1451 }, { "file": "test/Gas.t.sol", @@ -251,17 +281,29 @@ "name": "MUD storage load (warm, 1 word)", "gasUsed": 412 }, + { + "file": "test/GasStorageLoad.t.sol", + "test": "testCompareStorageLoadMUD", + "name": "MUD storage load field (warm, 1 word)", + "gasUsed": 245 + }, { "file": "test/GasStorageLoad.t.sol", "test": "testCompareStorageLoadMUD", "name": "MUD storage load (warm, 1 word, partial)", "gasUsed": 460 }, + { + "file": "test/GasStorageLoad.t.sol", + "test": "testCompareStorageLoadMUD", + "name": "MUD storage load field (warm, 1 word, partial)", + "gasUsed": 378 + }, { "file": "test/GasStorageLoad.t.sol", "test": "testCompareStorageLoadMUD", "name": "MUD storage load (warm, 10 words)", - "gasUsed": 1914 + "gasUsed": 1916 }, { "file": "test/GasStorageLoad.t.sol", @@ -309,7 +351,7 @@ "file": "test/KeyEncoding.t.sol", "test": "testRegisterAndGetFieldLayout", "name": "register KeyEncoding table", - "gasUsed": 697905 + "gasUsed": 687791 }, { "file": "test/Mixed.t.sol", @@ -321,19 +363,19 @@ "file": "test/Mixed.t.sol", "test": "testRegisterAndGetFieldLayout", "name": "register Mixed table", - "gasUsed": 560027 + "gasUsed": 549609 }, { "file": "test/Mixed.t.sol", "test": "testSetAndGet", "name": "set record in Mixed", - "gasUsed": 108640 + "gasUsed": 103911 }, { "file": "test/Mixed.t.sol", "test": "testSetAndGet", "name": "get record from Mixed", - "gasUsed": 9640 + "gasUsed": 7181 }, { "file": "test/PackedCounter.t.sol", @@ -519,343 +561,343 @@ "file": "test/StoreCoreDynamic.t.sol", "test": "testGetFieldSlice", "name": "get field slice (cold, 1 slot)", - "gasUsed": 8425 + "gasUsed": 8282 }, { "file": "test/StoreCoreDynamic.t.sol", "test": "testGetFieldSlice", "name": "get field slice (warm, 1 slot)", - "gasUsed": 2493 + "gasUsed": 2350 }, { "file": "test/StoreCoreDynamic.t.sol", "test": "testGetFieldSlice", "name": "get field slice (semi-cold, 1 slot)", - "gasUsed": 4498 + "gasUsed": 4353 }, { "file": "test/StoreCoreDynamic.t.sol", "test": "testGetFieldSlice", "name": "get field slice (warm, 2 slots)", - "gasUsed": 4725 + "gasUsed": 4579 }, { "file": "test/StoreCoreDynamic.t.sol", "test": "testGetSecondFieldLength", "name": "get field length (cold, 1 slot)", - "gasUsed": 7966 + "gasUsed": 7757 }, { "file": "test/StoreCoreDynamic.t.sol", "test": "testGetSecondFieldLength", "name": "get field length (warm, 1 slot)", - "gasUsed": 1961 + "gasUsed": 1752 }, { "file": "test/StoreCoreDynamic.t.sol", "test": "testGetThirdFieldLength", "name": "get field length (warm due to , 2 slots)", - "gasUsed": 7965 + "gasUsed": 7756 }, { "file": "test/StoreCoreDynamic.t.sol", "test": "testGetThirdFieldLength", "name": "get field length (warm, 2 slots)", - "gasUsed": 1962 + "gasUsed": 1752 }, { "file": "test/StoreCoreDynamic.t.sol", "test": "testPopFromSecondField", "name": "pop from field (cold, 1 slot, 1 uint32 item)", - "gasUsed": 22898 + "gasUsed": 20239 }, { "file": "test/StoreCoreDynamic.t.sol", "test": "testPopFromSecondField", "name": "pop from field (warm, 1 slot, 1 uint32 item)", - "gasUsed": 16928 + "gasUsed": 14250 }, { "file": "test/StoreCoreDynamic.t.sol", "test": "testPopFromThirdField", "name": "pop from field (cold, 2 slots, 10 uint32 items)", - "gasUsed": 24647 + "gasUsed": 22434 }, { "file": "test/StoreCoreDynamic.t.sol", "test": "testPopFromThirdField", "name": "pop from field (warm, 2 slots, 10 uint32 items)", - "gasUsed": 16678 + "gasUsed": 14446 }, { "file": "test/StoreCoreGas.t.sol", "test": "testAccessEmptyData", "name": "access non-existing record", - "gasUsed": 6533 + "gasUsed": 6198 }, { "file": "test/StoreCoreGas.t.sol", "test": "testAccessEmptyData", "name": "access static field of non-existing record", - "gasUsed": 1481 + "gasUsed": 1318 }, { "file": "test/StoreCoreGas.t.sol", "test": "testAccessEmptyData", "name": "access dynamic field of non-existing record", - "gasUsed": 2449 + "gasUsed": 2045 }, { "file": "test/StoreCoreGas.t.sol", "test": "testAccessEmptyData", "name": "access length of dynamic field of non-existing record", - "gasUsed": 1335 + "gasUsed": 1121 }, { "file": "test/StoreCoreGas.t.sol", "test": "testAccessEmptyData", "name": "access slice of dynamic field of non-existing record", - "gasUsed": 1631 + "gasUsed": 1484 }, { "file": "test/StoreCoreGas.t.sol", "test": "testDeleteData", "name": "delete record (complex data, 3 slots)", - "gasUsed": 9470 + "gasUsed": 6691 }, { "file": "test/StoreCoreGas.t.sol", "test": "testHasFieldLayout", "name": "Check for existence of table (existent)", - "gasUsed": 4465 + "gasUsed": 1260 }, { "file": "test/StoreCoreGas.t.sol", "test": "testHasFieldLayout", "name": "check for existence of table (non-existent)", - "gasUsed": 6468 + "gasUsed": 3261 }, { "file": "test/StoreCoreGas.t.sol", "test": "testHooks", "name": "register subscriber", - "gasUsed": 61935 + "gasUsed": 60413 }, { "file": "test/StoreCoreGas.t.sol", "test": "testHooks", "name": "set record on table with subscriber", - "gasUsed": 73748 + "gasUsed": 71044 }, { "file": "test/StoreCoreGas.t.sol", "test": "testHooks", "name": "set static field on table with subscriber", - "gasUsed": 25441 + "gasUsed": 20820 }, { "file": "test/StoreCoreGas.t.sol", "test": "testHooks", "name": "delete record on table with subscriber", - "gasUsed": 21483 + "gasUsed": 16415 }, { "file": "test/StoreCoreGas.t.sol", "test": "testHooksDynamicData", "name": "register subscriber", - "gasUsed": 61935 + "gasUsed": 60413 }, { "file": "test/StoreCoreGas.t.sol", "test": "testHooksDynamicData", "name": "set (dynamic) record on table with subscriber", - "gasUsed": 167808 + "gasUsed": 164166 }, { "file": "test/StoreCoreGas.t.sol", "test": "testHooksDynamicData", "name": "set (dynamic) field on table with subscriber", - "gasUsed": 28269 + "gasUsed": 24020 }, { "file": "test/StoreCoreGas.t.sol", "test": "testHooksDynamicData", "name": "delete (dynamic) record on table with subscriber", - "gasUsed": 22901 + "gasUsed": 17400 }, { "file": "test/StoreCoreGas.t.sol", "test": "testPushToField", "name": "push to field (1 slot, 1 uint32 item)", - "gasUsed": 14558 + "gasUsed": 11994 }, { "file": "test/StoreCoreGas.t.sol", "test": "testPushToField", "name": "push to field (2 slots, 10 uint32 items)", - "gasUsed": 37206 + "gasUsed": 34748 }, { "file": "test/StoreCoreGas.t.sol", "test": "testRegisterAndGetFieldLayout", "name": "StoreCore: register table", - "gasUsed": 620356 + "gasUsed": 609744 }, { "file": "test/StoreCoreGas.t.sol", "test": "testRegisterAndGetFieldLayout", "name": "StoreCore: get field layout (warm)", - "gasUsed": 4479 + "gasUsed": 1272 }, { "file": "test/StoreCoreGas.t.sol", "test": "testRegisterAndGetFieldLayout", "name": "StoreCore: get value schema (warm)", - "gasUsed": 5033 + "gasUsed": 1782 }, { "file": "test/StoreCoreGas.t.sol", "test": "testRegisterAndGetFieldLayout", "name": "StoreCore: get key schema (warm)", - "gasUsed": 9281 + "gasUsed": 2816 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetDynamicData", "name": "set complex record with dynamic data (4 slots)", - "gasUsed": 104856 + "gasUsed": 101831 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetDynamicData", "name": "get complex record with dynamic data (4 slots)", - "gasUsed": 5105 + "gasUsed": 4469 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetDynamicData", "name": "compare: Set complex record with dynamic data using native solidity", - "gasUsed": 116842 + "gasUsed": 116845 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetDynamicData", "name": "compare: Set complex record with dynamic data using abi.encode", - "gasUsed": 267372 + "gasUsed": 267368 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetDynamicDataLength", "name": "set dynamic length of dynamic index 0", - "gasUsed": 23082 + "gasUsed": 22870 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetDynamicDataLength", "name": "set dynamic length of dynamic index 1", - "gasUsed": 1183 + "gasUsed": 970 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetDynamicDataLength", "name": "reduce dynamic length of dynamic index 0", - "gasUsed": 1174 + "gasUsed": 960 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetField", "name": "set static field (1 slot)", - "gasUsed": 33831 + "gasUsed": 31550 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetField", "name": "get static field (1 slot)", - "gasUsed": 1482 + "gasUsed": 1318 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetField", "name": "set static field (overlap 2 slot)", - "gasUsed": 32479 + "gasUsed": 30190 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetField", "name": "get static field (overlap 2 slot)", - "gasUsed": 2010 + "gasUsed": 1844 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetField", "name": "set dynamic field (1 slot, first dynamic field)", - "gasUsed": 55195 + "gasUsed": 53074 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetField", "name": "get dynamic field (1 slot, first dynamic field)", - "gasUsed": 2627 + "gasUsed": 2214 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetField", "name": "set dynamic field (1 slot, second dynamic field)", - "gasUsed": 33313 + "gasUsed": 31300 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetField", "name": "get dynamic field (1 slot, second dynamic field)", - "gasUsed": 2635 + "gasUsed": 2216 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetStaticData", "name": "set static record (1 slot)", - "gasUsed": 34181 + "gasUsed": 32137 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetStaticData", "name": "get static record (1 slot)", - "gasUsed": 1734 + "gasUsed": 1608 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetStaticDataSpanningWords", "name": "set static record (2 slots)", - "gasUsed": 56684 + "gasUsed": 54641 }, { "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetStaticDataSpanningWords", "name": "get static record (2 slots)", - "gasUsed": 1922 + "gasUsed": 1796 }, { "file": "test/StoreCoreGas.t.sol", "test": "testUpdateInField", "name": "update in field (1 slot, 1 uint32 item)", - "gasUsed": 14835 + "gasUsed": 13032 }, { "file": "test/StoreCoreGas.t.sol", "test": "testUpdateInField", "name": "push to field (2 slots, 6 uint64 items)", - "gasUsed": 15865 + "gasUsed": 13834 }, { "file": "test/StoreHook.t.sol", "test": "testCallHook", "name": "call an enabled hook", - "gasUsed": 10157 + "gasUsed": 14598 }, { "file": "test/StoreHook.t.sol", "test": "testCallHook", "name": "call a disabled hook", - "gasUsed": 144 + "gasUsed": 133 }, { "file": "test/StoreHook.t.sol", @@ -891,121 +933,121 @@ "file": "test/tables/Callbacks.t.sol", "test": "testSetAndGet", "name": "Callbacks: set field", - "gasUsed": 59331 + "gasUsed": 56142 }, { "file": "test/tables/Callbacks.t.sol", "test": "testSetAndGet", "name": "Callbacks: get field (warm)", - "gasUsed": 5160 + "gasUsed": 2894 }, { "file": "test/tables/Callbacks.t.sol", "test": "testSetAndGet", "name": "Callbacks: push 1 element", - "gasUsed": 39001 + "gasUsed": 35052 }, { "file": "test/tables/StoreHooks.t.sol", "test": "testOneSlot", "name": "StoreHooks: set field with one elements (cold)", - "gasUsed": 61323 + "gasUsed": 58148 }, { "file": "test/tables/StoreHooks.t.sol", "test": "testTable", "name": "StoreHooks: set field (cold)", - "gasUsed": 61323 + "gasUsed": 58147 }, { "file": "test/tables/StoreHooks.t.sol", "test": "testTable", "name": "StoreHooks: get field (warm)", - "gasUsed": 5136 + "gasUsed": 2896 }, { "file": "test/tables/StoreHooks.t.sol", "test": "testTable", "name": "StoreHooks: push 1 element (cold)", - "gasUsed": 19075 + "gasUsed": 15147 }, { "file": "test/tables/StoreHooks.t.sol", "test": "testTable", "name": "StoreHooks: pop 1 element (warm)", - "gasUsed": 15453 + "gasUsed": 11702 }, { "file": "test/tables/StoreHooks.t.sol", "test": "testTable", "name": "StoreHooks: push 1 element (warm)", - "gasUsed": 17134 + "gasUsed": 13172 }, { "file": "test/tables/StoreHooks.t.sol", "test": "testTable", "name": "StoreHooks: update 1 element (warm)", - "gasUsed": 37483 + "gasUsed": 34269 }, { "file": "test/tables/StoreHooks.t.sol", "test": "testTable", "name": "StoreHooks: delete record (warm)", - "gasUsed": 10992 + "gasUsed": 7102 }, { "file": "test/tables/StoreHooks.t.sol", "test": "testTable", "name": "StoreHooks: set field (warm)", - "gasUsed": 33544 + "gasUsed": 30308 }, { "file": "test/tables/StoreHooks.t.sol", "test": "testThreeSlots", "name": "StoreHooks: set field with three elements (cold)", - "gasUsed": 84011 + "gasUsed": 80838 }, { "file": "test/tables/StoreHooks.t.sol", "test": "testTwoSlots", "name": "StoreHooks: set field with two elements (cold)", - "gasUsed": 83923 + "gasUsed": 80750 }, { "file": "test/tables/StoreHooksColdLoad.t.sol", "test": "testDelete", "name": "StoreHooks: delete record (cold)", - "gasUsed": 19792 + "gasUsed": 15967 }, { "file": "test/tables/StoreHooksColdLoad.t.sol", "test": "testGet", "name": "StoreHooks: get field (cold)", - "gasUsed": 11129 + "gasUsed": 8893 }, { "file": "test/tables/StoreHooksColdLoad.t.sol", "test": "testGetItem", "name": "StoreHooks: get 1 element (cold)", - "gasUsed": 7644 + "gasUsed": 6521 }, { "file": "test/tables/StoreHooksColdLoad.t.sol", "test": "testLength", "name": "StoreHooks: get length (cold)", - "gasUsed": 7127 + "gasUsed": 5853 }, { "file": "test/tables/StoreHooksColdLoad.t.sol", "test": "testPop", "name": "StoreHooks: pop 1 element (cold)", - "gasUsed": 25580 + "gasUsed": 22188 }, { "file": "test/tables/StoreHooksColdLoad.t.sol", "test": "testUpdate", "name": "StoreHooks: update 1 element (cold)", - "gasUsed": 27157 + "gasUsed": 24324 }, { "file": "test/tightcoder/DecodeSlice.t.sol", @@ -1059,18 +1101,18 @@ "file": "test/Vector2.t.sol", "test": "testRegisterAndGetFieldLayout", "name": "register Vector2 field layout", - "gasUsed": 421491 + "gasUsed": 411032 }, { "file": "test/Vector2.t.sol", "test": "testSetAndGet", "name": "set Vector2 record", - "gasUsed": 36809 + "gasUsed": 33040 }, { "file": "test/Vector2.t.sol", "test": "testSetAndGet", "name": "get Vector2 record", - "gasUsed": 4472 + "gasUsed": 2530 } ] diff --git a/packages/store/src/Hook.sol b/packages/store/src/Hook.sol index 876f7e7e52..2d7da97a6f 100644 --- a/packages/store/src/Hook.sol +++ b/packages/store/src/Hook.sol @@ -21,7 +21,7 @@ library HookLib { * Filter the given hook from the hook list at the given key in the given hook table */ function filterListByAddress(bytes32 hookTableId, bytes32 key, address hookAddressToRemove) internal { - bytes21[] memory currentHooks = Hooks.get(hookTableId, key); + bytes21[] memory currentHooks = Hooks._get(hookTableId, key); // 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); @@ -44,7 +44,7 @@ library HookLib { } // Set the new hooks table - Hooks.set(hookTableId, key, newHooks); + Hooks._set(hookTableId, key, newHooks); } } diff --git a/packages/store/src/IStore.sol b/packages/store/src/IStore.sol index 68df303f9e..b13097f3a4 100644 --- a/packages/store/src/IStore.sol +++ b/packages/store/src/IStore.sol @@ -2,6 +2,7 @@ pragma solidity >=0.8.0; import { IStoreErrors } from "./IStoreErrors.sol"; +import { PackedCounter } from "./PackedCounter.sol"; import { FieldLayout } from "./FieldLayout.sol"; import { Schema } from "./Schema.sol"; import { IStoreHook } from "./IStoreHook.sol"; @@ -13,34 +14,65 @@ interface IStoreRead { function getKeySchema(bytes32 tableId) external view returns (Schema keySchema); - // Get full record (including full array) + /** + * Get full record (all fields, static and dynamic data) for the given tableId and key tuple, with the given value field layout + */ function getRecord( bytes32 tableId, bytes32[] calldata keyTuple, FieldLayout fieldLayout ) external view returns (bytes memory data); - // Get partial data at schema index + /** + * Get a single field from the given tableId and key tuple, with the given value field layout + */ function getField( bytes32 tableId, bytes32[] calldata keyTuple, - uint8 schemaIndex, + uint8 fieldIndex, FieldLayout fieldLayout ) external view returns (bytes memory data); - // Get field length at schema index + /** + * Get a single static field from the given tableId and key tuple, with the given value field layout. + * Note: the field value is left-aligned in the returned bytes32, the rest of the word is not zeroed out. + * Consumers are expected to truncate the returned value as needed. + */ + function getStaticField( + bytes32 tableId, + bytes32[] calldata keyTuple, + uint8 fieldIndex, + FieldLayout fieldLayout + ) external view returns (bytes32); + + /** + * Get a single dynamic field from the given tableId and key tuple at the given dynamic field index. + * (Dynamic field index = field index - number of static fields) + */ + function getDynamicField( + bytes32 tableId, + bytes32[] memory keyTuple, + uint8 dynamicFieldIndex + ) external view returns (bytes memory); + + /** + * Get the byte length of a single field from the given tableId and key tuple, with the given value field layout + */ function getFieldLength( bytes32 tableId, bytes32[] memory keyTuple, - uint8 schemaIndex, + uint8 fieldIndex, FieldLayout fieldLayout ) external view returns (uint256); - // Get start:end slice of the field at schema index + /** + * Get a byte slice (including start, excluding end) of a single dynamic field from the given tableId and key tuple, with the given value field layout. + * The slice is unchecked and will return invalid data if `start`:`end` overflow. + */ function getFieldSlice( bytes32 tableId, bytes32[] memory keyTuple, - uint8 schemaIndex, + uint8 fieldIndex, FieldLayout fieldLayout, uint256 start, uint256 end @@ -48,15 +80,37 @@ interface IStoreRead { } interface IStoreWrite { - event StoreSetRecord(bytes32 tableId, bytes32[] keyTuple, bytes data); - event StoreSetField(bytes32 tableId, bytes32[] keyTuple, uint8 schemaIndex, bytes data); - event StoreDeleteRecord(bytes32 tableId, bytes32[] keyTuple); + event StoreSetRecord( + bytes32 indexed tableId, + bytes32[] keyTuple, + bytes staticData, + bytes32 encodedLengths, + bytes dynamicData + ); + event StoreSpliceStaticData( + bytes32 indexed tableId, + bytes32[] keyTuple, + uint48 start, + uint40 deleteCount, + bytes data + ); + event StoreSpliceDynamicData( + bytes32 indexed tableId, + bytes32[] keyTuple, + uint48 start, + uint40 deleteCount, + bytes data, + bytes32 encodedLengths + ); + event StoreDeleteRecord(bytes32 indexed tableId, bytes32[] keyTuple); // Set full record (including full dynamic data) function setRecord( bytes32 tableId, bytes32[] calldata keyTuple, - bytes calldata data, + bytes calldata staticData, + PackedCounter encodedLengths, + bytes calldata dynamicData, FieldLayout fieldLayout ) external; @@ -64,7 +118,7 @@ interface IStoreWrite { function setField( bytes32 tableId, bytes32[] calldata keyTuple, - uint8 schemaIndex, + uint8 fieldIndex, bytes calldata data, FieldLayout fieldLayout ) external; @@ -73,7 +127,7 @@ interface IStoreWrite { function pushToField( bytes32 tableId, bytes32[] calldata keyTuple, - uint8 schemaIndex, + uint8 fieldIndex, bytes calldata dataToPush, FieldLayout fieldLayout ) external; @@ -82,7 +136,7 @@ interface IStoreWrite { function popFromField( bytes32 tableId, bytes32[] calldata keyTuple, - uint8 schemaIndex, + uint8 fieldIndex, uint256 byteLengthToPop, FieldLayout fieldLayout ) external; @@ -91,7 +145,7 @@ interface IStoreWrite { function updateInField( bytes32 tableId, bytes32[] calldata keyTuple, - uint8 schemaIndex, + uint8 fieldIndex, uint256 startByteIndex, bytes calldata dataToSet, FieldLayout fieldLayout @@ -102,13 +156,21 @@ interface IStoreWrite { } interface IStoreEphemeral { - event StoreEphemeralRecord(bytes32 tableId, bytes32[] keyTuple, bytes data); + event StoreEphemeralRecord( + bytes32 indexed tableId, + bytes32[] keyTuple, + bytes staticData, + bytes32 encodedLengths, + bytes dynamicData + ); // Emit the ephemeral event without modifying storage function emitEphemeralRecord( bytes32 tableId, bytes32[] calldata keyTuple, - bytes calldata data, + bytes calldata staticData, + PackedCounter encodedLengths, + bytes calldata dynamicData, FieldLayout fieldLayout ) external; } diff --git a/packages/store/src/IStoreErrors.sol b/packages/store/src/IStoreErrors.sol index 8763723b5d..e5214d19cd 100644 --- a/packages/store/src/IStoreErrors.sol +++ b/packages/store/src/IStoreErrors.sol @@ -8,7 +8,8 @@ interface IStoreErrors { error StoreCore_NotImplemented(); error StoreCore_NotDynamicField(); - error StoreCore_InvalidDataLength(uint256 expected, uint256 received); + error StoreCore_InvalidStaticDataLength(uint256 expected, uint256 received); + error StoreCore_InvalidDynamicDataLength(uint256 expected, uint256 received); error StoreCore_InvalidKeyNamesLength(uint256 expected, uint256 received); error StoreCore_InvalidFieldNamesLength(uint256 expected, uint256 received); error StoreCore_InvalidValueSchemaLength(uint256 expected, uint256 received); diff --git a/packages/store/src/IStoreHook.sol b/packages/store/src/IStoreHook.sol index fa65ff4cff..c3132fcea7 100644 --- a/packages/store/src/IStoreHook.sol +++ b/packages/store/src/IStoreHook.sol @@ -3,6 +3,7 @@ pragma solidity >=0.8.0; import { FieldLayout } from "./FieldLayout.sol"; import { IERC165, ERC165_INTERFACE_ID } from "./IERC165.sol"; +import { PackedCounter } from "./PackedCounter.sol"; // ERC-165 Interface ID (see https://eips.ethereum.org/EIPS/eip-165) bytes4 constant STORE_HOOK_INTERFACE_ID = IStoreHook.onBeforeSetRecord.selector ^ @@ -17,21 +18,25 @@ interface IStoreHook is IERC165 { function onBeforeSetRecord( bytes32 tableId, bytes32[] memory keyTuple, - bytes memory data, + bytes calldata staticData, + PackedCounter encodedLengths, + bytes calldata dynamicData, FieldLayout fieldLayout ) external; function onAfterSetRecord( bytes32 tableId, bytes32[] memory keyTuple, - bytes memory data, + bytes calldata staticData, + PackedCounter encodedLengths, + bytes calldata dynamicData, FieldLayout fieldLayout ) external; function onBeforeSetField( bytes32 tableId, bytes32[] memory keyTuple, - uint8 schemaIndex, + uint8 fieldIndex, bytes memory data, FieldLayout fieldLayout ) external; @@ -39,7 +44,7 @@ interface IStoreHook is IERC165 { function onAfterSetField( bytes32 tableId, bytes32[] memory keyTuple, - uint8 schemaIndex, + uint8 fieldIndex, bytes memory data, FieldLayout fieldLayout ) external; diff --git a/packages/store/src/Storage.sol b/packages/store/src/Storage.sol index 0961cf4d20..427e45748f 100644 --- a/packages/store/src/Storage.sol +++ b/packages/store/src/Storage.sol @@ -209,4 +209,39 @@ library Storage { } } } + + /** + * Load up to 32 bytes from storage at the given storagePointer and offset. + * The return value is left-aligned, the bytes beyond the length are not zeroed out, + * and the caller is expected to truncate as needed. + * Since fields are tightly packed, they can span more than one slot. + * Since the they're max 32 bytes, they can span at most 2 slots. + */ + function loadField(uint256 storagePointer, uint256 length, uint256 offset) internal view returns (bytes32 result) { + if (offset >= 32) { + unchecked { + storagePointer += offset / 32; + offset %= 32; + } + } + + // Extra data past length is not truncated + // This assumes that the caller will handle the overflow bits appropriately + assembly { + result := shl(mul(offset, 8), sload(storagePointer)) + } + + uint256 wordRemainder; + // (safe because of `offset %= 32` at the start) + unchecked { + wordRemainder = 32 - offset; + } + + // Read from the next slot if field spans 2 slots + if (length > wordRemainder) { + assembly { + result := or(result, shr(mul(wordRemainder, 8), sload(add(storagePointer, 1)))) + } + } + } } diff --git a/packages/store/src/StoreCore.sol b/packages/store/src/StoreCore.sol index c2b81a57a8..8b95cebd92 100644 --- a/packages/store/src/StoreCore.sol +++ b/packages/store/src/StoreCore.sol @@ -17,10 +17,36 @@ import { StoreHookLib, StoreHookType } from "./StoreHook.sol"; library StoreCore { // note: the preimage of the tuple of keys used to index is part of the event, so it can be used by indexers - event StoreSetRecord(bytes32 tableId, bytes32[] keyTuple, bytes data); - event StoreSetField(bytes32 tableId, bytes32[] keyTuple, uint8 fieldIndex, bytes data); - event StoreDeleteRecord(bytes32 tableId, bytes32[] keyTuple); - event StoreEphemeralRecord(bytes32 tableId, bytes32[] keyTuple, bytes data); + event StoreSetRecord( + bytes32 indexed tableId, + bytes32[] keyTuple, + bytes staticData, + bytes32 encodedLengths, + bytes dynamicData + ); + event StoreSpliceStaticData( + bytes32 indexed tableId, + bytes32[] keyTuple, + uint48 start, + uint40 deleteCount, + bytes data + ); + event StoreSpliceDynamicData( + bytes32 indexed tableId, + bytes32[] keyTuple, + uint48 start, + uint40 deleteCount, + bytes data, + bytes32 encodedLengths + ); + event StoreDeleteRecord(bytes32 indexed tableId, bytes32[] keyTuple); + event StoreEphemeralRecord( + bytes32 indexed tableId, + bytes32[] keyTuple, + bytes staticData, + bytes32 encodedLengths, + bytes dynamicData + ); /** * Intialize the store address to use in StoreSwitch. @@ -54,7 +80,7 @@ library StoreCore { * Get the field layout for the given tableId */ function getFieldLayout(bytes32 tableId) internal view returns (FieldLayout fieldLayout) { - fieldLayout = FieldLayout.wrap(Tables.getFieldLayout(tableId)); + fieldLayout = FieldLayout.wrap(Tables._getFieldLayout(tableId)); if (fieldLayout.isEmpty()) { revert IStoreErrors.StoreCore_TableNotFound(tableId, string(abi.encodePacked(tableId))); } @@ -64,7 +90,7 @@ library StoreCore { * Get the key schema for the given tableId */ function getKeySchema(bytes32 tableId) internal view returns (Schema keySchema) { - keySchema = Schema.wrap(Tables.getKeySchema(tableId)); + keySchema = Schema.wrap(Tables._getKeySchema(tableId)); // key schemas can be empty for singleton tables, so we can't depend on key schema for table check if (!hasTable(tableId)) { revert IStoreErrors.StoreCore_TableNotFound(tableId, string(abi.encodePacked(tableId))); @@ -75,7 +101,7 @@ library StoreCore { * Get the schema for the given tableId */ function getValueSchema(bytes32 tableId) internal view returns (Schema valueSchema) { - valueSchema = Schema.wrap(Tables.getValueSchema(tableId)); + valueSchema = Schema.wrap(Tables._getValueSchema(tableId)); if (valueSchema.isEmpty()) { revert IStoreErrors.StoreCore_TableNotFound(tableId, string(abi.encodePacked(tableId))); } @@ -85,7 +111,7 @@ library StoreCore { * Check if a table with the given tableId exists */ function hasTable(bytes32 tableId) internal view returns (bool) { - return Tables.getFieldLayout(tableId) != bytes32(0); + return Tables._getFieldLayout(tableId) != bytes32(0); } /** @@ -101,6 +127,7 @@ library StoreCore { ) internal { // Verify the field layout is valid fieldLayout.validate({ allowEmpty: false }); + // Verify the schema is valid keySchema.validate({ allowEmpty: true }); valueSchema.validate({ allowEmpty: false }); @@ -114,6 +141,7 @@ library StoreCore { if (fieldNames.length != fieldLayout.numFields()) { revert IStoreErrors.StoreCore_InvalidFieldNamesLength(fieldLayout.numFields(), fieldNames.length); } + // Verify the number of value schema types if (valueSchema.numFields() != fieldLayout.numFields()) { revert IStoreErrors.StoreCore_InvalidValueSchemaLength(fieldLayout.numFields(), valueSchema.numFields()); @@ -125,7 +153,7 @@ library StoreCore { } // Register the table metadata - Tables.set( + Tables._set( tableId, FieldLayout.unwrap(fieldLayout), Schema.unwrap(keySchema), @@ -164,48 +192,64 @@ library StoreCore { /** * Set full data record for the given tableId and key tuple and field layout */ - function setRecord(bytes32 tableId, bytes32[] memory keyTuple, bytes memory data, FieldLayout fieldLayout) internal { - // verify the value has the correct length for the table (based on the table's field layout) + function setRecord( + bytes32 tableId, + bytes32[] memory keyTuple, + bytes memory staticData, + PackedCounter encodedLengths, + bytes memory dynamicData, + FieldLayout fieldLayout + ) internal { + // verify the value has the correct length for the tableId (based on the tableId's field layout) // to prevent invalid data from being stored // Verify static data length + dynamic data length matches the given data - (uint256 staticLength, PackedCounter dynamicLength) = StoreCoreInternal._validateDataLength(fieldLayout, data); + StoreCoreInternal._validateDataLength(fieldLayout, staticData, encodedLengths, dynamicData); // Emit event to notify indexers - emit StoreSetRecord(tableId, keyTuple, data); + emit StoreSetRecord(tableId, keyTuple, staticData, encodedLengths.unwrap(), dynamicData); // Call onBeforeSetRecord hooks (before actually modifying the state, so observers have access to the previous state if needed) - bytes21[] memory hooks = StoreHooks.get(tableId); + bytes21[] memory hooks = StoreHooks._get(tableId); for (uint256 i; i < hooks.length; i++) { Hook hook = Hook.wrap(hooks[i]); if (hook.isEnabled(uint8(StoreHookType.BEFORE_SET_RECORD))) { - IStoreHook(hook.getAddress()).onBeforeSetRecord(tableId, keyTuple, data, fieldLayout); + IStoreHook(hook.getAddress()).onBeforeSetRecord( + tableId, + keyTuple, + staticData, + encodedLengths, + dynamicData, + fieldLayout + ); } } // Store the static data at the static data location uint256 staticDataLocation = StoreCoreInternal._getStaticDataLocation(tableId, keyTuple); - uint256 memoryPointer = Memory.dataPointer(data); + uint256 memoryPointer = Memory.dataPointer(staticData); Storage.store({ storagePointer: staticDataLocation, offset: 0, memoryPointer: memoryPointer, - length: staticLength + length: staticData.length }); - memoryPointer += staticLength + 32; // move the memory pointer to the start of the dynamic data (skip the encoded dynamic length) // Set the dynamic data if there are dynamic fields if (fieldLayout.numDynamicFields() > 0) { // Store the dynamic data length at the dynamic data length location uint256 dynamicDataLengthLocation = StoreCoreInternal._getDynamicDataLengthLocation(tableId, keyTuple); - Storage.store({ storagePointer: dynamicDataLengthLocation, data: dynamicLength.unwrap() }); + Storage.store({ storagePointer: dynamicDataLengthLocation, data: encodedLengths.unwrap() }); + + // Move the memory pointer to the start of the dynamic data + memoryPointer = Memory.dataPointer(dynamicData); // For every dynamic element, slice off the dynamic data and store it at the dynamic location uint256 dynamicDataLocation; uint256 dynamicDataLength; for (uint8 i; i < fieldLayout.numDynamicFields(); ) { dynamicDataLocation = StoreCoreInternal._getDynamicDataLocation(tableId, keyTuple, i); - dynamicDataLength = dynamicLength.atIndex(i); + dynamicDataLength = encodedLengths.atIndex(i); Storage.store({ storagePointer: dynamicDataLocation, offset: 0, @@ -223,7 +267,14 @@ library StoreCore { for (uint256 i; i < hooks.length; i++) { Hook hook = Hook.wrap(hooks[i]); if (hook.isEnabled(uint8(StoreHookType.AFTER_SET_RECORD))) { - IStoreHook(hook.getAddress()).onAfterSetRecord(tableId, keyTuple, data, fieldLayout); + IStoreHook(hook.getAddress()).onAfterSetRecord( + tableId, + keyTuple, + staticData, + encodedLengths, + dynamicData, + fieldLayout + ); } } } @@ -234,33 +285,30 @@ library StoreCore { function setField( bytes32 tableId, bytes32[] memory keyTuple, - uint8 schemaIndex, + uint8 fieldIndex, bytes memory data, FieldLayout fieldLayout ) internal { - // Emit event to notify indexers - emit StoreSetField(tableId, keyTuple, schemaIndex, data); - // Call onBeforeSetField hooks (before modifying the state) - bytes21[] memory hooks = StoreHooks.get(tableId); + bytes21[] memory hooks = StoreHooks._get(tableId); for (uint256 i; i < hooks.length; i++) { Hook hook = Hook.wrap(hooks[i]); if (hook.isEnabled(uint8(StoreHookType.BEFORE_SET_FIELD))) { - IStoreHook(hook.getAddress()).onBeforeSetField(tableId, keyTuple, schemaIndex, data, fieldLayout); + IStoreHook(hook.getAddress()).onBeforeSetField(tableId, keyTuple, fieldIndex, data, fieldLayout); } } - if (schemaIndex < fieldLayout.numStaticFields()) { - StoreCoreInternal._setStaticField(tableId, keyTuple, fieldLayout, schemaIndex, data); + if (fieldIndex < fieldLayout.numStaticFields()) { + StoreCoreInternal._setStaticField(tableId, keyTuple, fieldLayout, fieldIndex, data); } else { - StoreCoreInternal._setDynamicField(tableId, keyTuple, fieldLayout, schemaIndex, data); + StoreCoreInternal._setDynamicField(tableId, keyTuple, fieldLayout, fieldIndex, data); } // Call onAfterSetField hooks (after modifying the state) for (uint256 i; i < hooks.length; i++) { Hook hook = Hook.wrap(hooks[i]); if (hook.isEnabled(uint8(StoreHookType.AFTER_SET_FIELD))) { - IStoreHook(hook.getAddress()).onAfterSetField(tableId, keyTuple, schemaIndex, data, fieldLayout); + IStoreHook(hook.getAddress()).onAfterSetField(tableId, keyTuple, fieldIndex, data, fieldLayout); } } } @@ -273,7 +321,7 @@ library StoreCore { emit StoreDeleteRecord(tableId, keyTuple); // Call onBeforeDeleteRecord hooks (before actually modifying the state, so observers have access to the previous state if needed) - bytes21[] memory hooks = StoreHooks.get(tableId); + bytes21[] memory hooks = StoreHooks._get(tableId); for (uint256 i; i < hooks.length; i++) { Hook hook = Hook.wrap(hooks[i]); if (hook.isEnabled(uint8(StoreHookType.BEFORE_DELETE_RECORD))) { @@ -306,39 +354,36 @@ library StoreCore { function pushToField( bytes32 tableId, bytes32[] memory keyTuple, - uint8 schemaIndex, + uint8 fieldIndex, bytes memory dataToPush, FieldLayout fieldLayout ) internal { - if (schemaIndex < fieldLayout.numStaticFields()) { + if (fieldIndex < fieldLayout.numStaticFields()) { revert IStoreErrors.StoreCore_NotDynamicField(); } - // TODO add push-specific event and hook to avoid the storage read? (https://github.com/latticexyz/mud/issues/444) + // TODO add push-specific hook to avoid the storage read? (https://github.com/latticexyz/mud/issues/444) bytes memory fullData = abi.encodePacked( - StoreCoreInternal._getDynamicField(tableId, keyTuple, schemaIndex, fieldLayout), + getDynamicField(tableId, keyTuple, fieldIndex - uint8(fieldLayout.numStaticFields())), dataToPush ); - // Emit event to notify indexers - emit StoreSetField(tableId, keyTuple, schemaIndex, fullData); - // Call onBeforeSetField hooks (before modifying the state) - bytes21[] memory hooks = StoreHooks.get(tableId); + bytes21[] memory hooks = StoreHooks._get(tableId); for (uint256 i; i < hooks.length; i++) { Hook hook = Hook.wrap(hooks[i]); if (hook.isEnabled(uint8(StoreHookType.BEFORE_SET_FIELD))) { - IStoreHook(hook.getAddress()).onBeforeSetField(tableId, keyTuple, schemaIndex, fullData, fieldLayout); + IStoreHook(hook.getAddress()).onBeforeSetField(tableId, keyTuple, fieldIndex, fullData, fieldLayout); } } - StoreCoreInternal._pushToDynamicField(tableId, keyTuple, fieldLayout, schemaIndex, dataToPush); + StoreCoreInternal._pushToDynamicField(tableId, keyTuple, fieldLayout, fieldIndex, dataToPush); // Call onAfterSetField hooks (after modifying the state) for (uint256 i; i < hooks.length; i++) { Hook hook = Hook.wrap(hooks[i]); if (hook.isEnabled(uint8(StoreHookType.AFTER_SET_FIELD))) { - IStoreHook(hook.getAddress()).onAfterSetField(tableId, keyTuple, schemaIndex, fullData, fieldLayout); + IStoreHook(hook.getAddress()).onAfterSetField(tableId, keyTuple, fieldIndex, fullData, fieldLayout); } } } @@ -349,40 +394,37 @@ library StoreCore { function popFromField( bytes32 tableId, bytes32[] memory keyTuple, - uint8 schemaIndex, + uint8 fieldIndex, uint256 byteLengthToPop, FieldLayout fieldLayout ) internal { - if (schemaIndex < fieldLayout.numStaticFields()) { + if (fieldIndex < fieldLayout.numStaticFields()) { revert IStoreErrors.StoreCore_NotDynamicField(); } - // TODO add pop-specific event and hook to avoid the storage read? (https://github.com/latticexyz/mud/issues/444) + // TODO add pop-specific hook to avoid the storage read? (https://github.com/latticexyz/mud/issues/444) bytes memory fullData; { - bytes memory oldData = StoreCoreInternal._getDynamicField(tableId, keyTuple, schemaIndex, fieldLayout); + bytes memory oldData = getDynamicField(tableId, keyTuple, fieldIndex - uint8(fieldLayout.numStaticFields())); fullData = SliceLib.getSubslice(oldData, 0, oldData.length - byteLengthToPop).toBytes(); } - // Emit event to notify indexers - emit StoreSetField(tableId, keyTuple, schemaIndex, fullData); - // Call onBeforeSetField hooks (before modifying the state) - bytes21[] memory hooks = StoreHooks.get(tableId); + bytes21[] memory hooks = StoreHooks._get(tableId); for (uint256 i; i < hooks.length; i++) { Hook hook = Hook.wrap(hooks[i]); if (hook.isEnabled(uint8(StoreHookType.BEFORE_SET_FIELD))) { - IStoreHook(hook.getAddress()).onBeforeSetField(tableId, keyTuple, schemaIndex, fullData, fieldLayout); + IStoreHook(hook.getAddress()).onBeforeSetField(tableId, keyTuple, fieldIndex, fullData, fieldLayout); } } - StoreCoreInternal._popFromDynamicField(tableId, keyTuple, fieldLayout, schemaIndex, byteLengthToPop); + StoreCoreInternal._popFromDynamicField(tableId, keyTuple, fieldLayout, fieldIndex, byteLengthToPop); // Call onAfterSetField hooks (after modifying the state) for (uint256 i; i < hooks.length; i++) { Hook hook = Hook.wrap(hooks[i]); if (hook.isEnabled(uint8(StoreHookType.AFTER_SET_FIELD))) { - IStoreHook(hook.getAddress()).onAfterSetField(tableId, keyTuple, schemaIndex, fullData, fieldLayout); + IStoreHook(hook.getAddress()).onAfterSetField(tableId, keyTuple, fieldIndex, fullData, fieldLayout); } } } @@ -393,24 +435,25 @@ library StoreCore { function updateInField( bytes32 tableId, bytes32[] memory keyTuple, - uint8 schemaIndex, + uint8 fieldIndex, uint256 startByteIndex, bytes memory dataToSet, FieldLayout fieldLayout ) internal { - if (schemaIndex < fieldLayout.numStaticFields()) { + if (fieldIndex < fieldLayout.numStaticFields()) { revert IStoreErrors.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 (startByteIndex > type(uint40).max) { revert IStoreErrors.StoreCore_DataIndexOverflow(type(uint40).max, startByteIndex); } - // TODO add setItem-specific event and hook to avoid the storage read? (https://github.com/latticexyz/mud/issues/444) + // TODO add setItem-specific hook to avoid the storage read? (https://github.com/latticexyz/mud/issues/444) bytes memory fullData; { - bytes memory oldData = StoreCoreInternal._getDynamicField(tableId, keyTuple, schemaIndex, fieldLayout); + bytes memory oldData = getDynamicField(tableId, keyTuple, fieldIndex - uint8(fieldLayout.numStaticFields())); fullData = abi.encodePacked( SliceLib.getSubslice(oldData, 0, startByteIndex).toBytes(), dataToSet, @@ -418,25 +461,22 @@ library StoreCore { ); } - // Emit event to notify indexers - emit StoreSetField(tableId, keyTuple, schemaIndex, fullData); - // Call onBeforeSetField hooks (before modifying the state) - bytes21[] memory hooks = StoreHooks.get(tableId); + bytes21[] memory hooks = StoreHooks._get(tableId); for (uint256 i; i < hooks.length; i++) { Hook hook = Hook.wrap(hooks[i]); if (hook.isEnabled(uint8(StoreHookType.BEFORE_SET_FIELD))) { - IStoreHook(hook.getAddress()).onBeforeSetField(tableId, keyTuple, schemaIndex, fullData, fieldLayout); + IStoreHook(hook.getAddress()).onBeforeSetField(tableId, keyTuple, fieldIndex, fullData, fieldLayout); } } - StoreCoreInternal._setDynamicFieldItem(tableId, keyTuple, fieldLayout, schemaIndex, startByteIndex, dataToSet); + StoreCoreInternal._setDynamicFieldItem(tableId, keyTuple, fieldLayout, fieldIndex, startByteIndex, dataToSet); // Call onAfterSetField hooks (after modifying the state) for (uint256 i; i < hooks.length; i++) { Hook hook = Hook.wrap(hooks[i]); if (hook.isEnabled(uint8(StoreHookType.AFTER_SET_FIELD))) { - IStoreHook(hook.getAddress()).onAfterSetField(tableId, keyTuple, schemaIndex, fullData, fieldLayout); + IStoreHook(hook.getAddress()).onAfterSetField(tableId, keyTuple, fieldIndex, fullData, fieldLayout); } } } @@ -453,14 +493,16 @@ library StoreCore { function emitEphemeralRecord( bytes32 tableId, bytes32[] memory keyTuple, - bytes memory data, + bytes memory staticData, + PackedCounter encodedLengths, + bytes memory dynamicData, FieldLayout fieldLayout ) internal { // Verify static data length + dynamic data length matches the given data - StoreCoreInternal._validateDataLength(fieldLayout, data); + StoreCoreInternal._validateDataLength(fieldLayout, staticData, encodedLengths, dynamicData); // Emit event to notify indexers - emit StoreEphemeralRecord(tableId, keyTuple, data); + emit StoreEphemeralRecord(tableId, keyTuple, staticData, encodedLengths.unwrap(), dynamicData); } /************************************************************************ @@ -501,6 +543,7 @@ library StoreCore { // Early return if there are no dynamic fields if (dynamicDataLength.total() == 0) return data; + // Advance memoryPointer to the dynamic data section memoryPointer += staticLength; @@ -516,6 +559,7 @@ library StoreCore { uint256 dynamicDataLocation = StoreCoreInternal._getDynamicDataLocation(tableId, keyTuple, i); uint256 length = dynamicDataLength.atIndex(i); Storage.load({ storagePointer: dynamicDataLocation, length: length, offset: 0, memoryPointer: memoryPointer }); + // Advance memoryPointer by the length of this dynamic field memoryPointer += length; } @@ -530,31 +574,70 @@ library StoreCore { function getField( bytes32 tableId, bytes32[] memory keyTuple, - uint8 schemaIndex, + uint8 fieldIndex, FieldLayout fieldLayout ) internal view returns (bytes memory) { - if (schemaIndex < fieldLayout.numStaticFields()) { - return StoreCoreInternal._getStaticField(tableId, keyTuple, schemaIndex, fieldLayout); + if (fieldIndex < fieldLayout.numStaticFields()) { + return StoreCoreInternal._getStaticFieldBytes(tableId, keyTuple, fieldIndex, fieldLayout); } else { - return StoreCoreInternal._getDynamicField(tableId, keyTuple, schemaIndex, fieldLayout); + return getDynamicField(tableId, keyTuple, fieldIndex - uint8(fieldLayout.numStaticFields())); } } + /** + * Get a single static field from the given tableId and key tuple, with the given value field layout. + * Note: the field value is left-aligned in the returned bytes32, the rest of the word is not zeroed out. + * Consumers are expected to truncate the returned value as needed. + */ + function getStaticField( + bytes32 tableId, + bytes32[] memory keyTuple, + uint8 fieldIndex, + FieldLayout fieldLayout + ) internal view returns (bytes32) { + // Get the length, storage location and offset of the static field + // and load the data from storage + return + Storage.loadField({ + storagePointer: StoreCoreInternal._getStaticDataLocation(tableId, keyTuple), + length: fieldLayout.atIndex(fieldIndex), + offset: StoreCoreInternal._getStaticDataOffset(fieldLayout, fieldIndex) + }); + } + + /** + * Get a single dynamic field from the given tableId and key tuple, with the given value field layout + */ + function getDynamicField( + bytes32 tableId, + bytes32[] memory keyTuple, + uint8 dynamicFieldIndex + ) internal view returns (bytes memory) { + // Get the storage location of the dynamic field + // and load the data from storage + return + Storage.load({ + storagePointer: StoreCoreInternal._getDynamicDataLocation(tableId, keyTuple, dynamicFieldIndex), + length: StoreCoreInternal._loadEncodedDynamicDataLength(tableId, keyTuple).atIndex(dynamicFieldIndex), + offset: 0 + }); + } + /** * Get the byte length of a single field from the given tableId and key tuple, with the given value field layout */ function getFieldLength( bytes32 tableId, bytes32[] memory keyTuple, - uint8 schemaIndex, + uint8 fieldIndex, FieldLayout fieldLayout ) internal view returns (uint256) { uint8 numStaticFields = uint8(fieldLayout.numStaticFields()); - if (schemaIndex < numStaticFields) { - return fieldLayout.atIndex(schemaIndex); + if (fieldIndex < numStaticFields) { + return fieldLayout.atIndex(fieldIndex); } else { // Get the length and storage location of the dynamic field - uint8 dynamicFieldLayoutIndex = schemaIndex - numStaticFields; + uint8 dynamicFieldLayoutIndex = fieldIndex - numStaticFields; return StoreCoreInternal._loadEncodedDynamicDataLength(tableId, keyTuple).atIndex(dynamicFieldLayoutIndex); } } @@ -566,18 +649,18 @@ library StoreCore { function getFieldSlice( bytes32 tableId, bytes32[] memory keyTuple, - uint8 schemaIndex, + uint8 fieldIndex, FieldLayout fieldLayout, uint256 start, uint256 end ) internal view returns (bytes memory) { uint8 numStaticFields = uint8(fieldLayout.numStaticFields()); - if (schemaIndex < fieldLayout.numStaticFields()) { + if (fieldIndex < fieldLayout.numStaticFields()) { revert IStoreErrors.StoreCore_NotDynamicField(); } // Get the length and storage location of the dynamic field - uint8 dynamicSchemaIndex = schemaIndex - numStaticFields; + uint8 dynamicSchemaIndex = fieldIndex - numStaticFields; uint256 location = StoreCoreInternal._getDynamicDataLocation(tableId, keyTuple, dynamicSchemaIndex); return Storage.load({ storagePointer: location, length: end - start, offset: start }); @@ -586,6 +669,8 @@ library StoreCore { library StoreCoreInternal { bytes32 internal constant SLOT = keccak256("mud.store"); + bytes32 internal constant DYNMAIC_DATA_SLOT = keccak256("mud.store.dynamicData"); + bytes32 internal constant DYNAMIC_DATA_LENGTH_SLOT = keccak256("mud.store.dynamicDataLength"); /************************************************************************ * @@ -597,46 +682,76 @@ library StoreCoreInternal { bytes32 tableId, bytes32[] memory keyTuple, FieldLayout fieldLayout, - uint8 schemaIndex, + uint8 fieldIndex, bytes memory data ) internal { - // verify the value has the correct length for the field - uint256 staticByteLength = fieldLayout.atIndex(schemaIndex); - if (staticByteLength != data.length) { - revert IStoreErrors.StoreCore_InvalidDataLength(staticByteLength, data.length); - } - - // Store the provided value in storage uint256 location = _getStaticDataLocation(tableId, keyTuple); - uint256 offset = _getStaticDataOffset(fieldLayout, schemaIndex); + uint256 offset = _getStaticDataOffset(fieldLayout, fieldIndex); + Storage.store({ storagePointer: location, offset: offset, data: data }); + + // Emit event to notify indexers + emit StoreCore.StoreSpliceStaticData({ + tableId: tableId, + keyTuple: keyTuple, + start: uint48(offset), + deleteCount: uint40(fieldLayout.atIndex(fieldIndex)), + data: data + }); } function _setDynamicField( bytes32 tableId, bytes32[] memory keyTuple, FieldLayout fieldLayout, - uint8 schemaIndex, + uint8 fieldIndex, bytes memory data ) internal { - uint8 dynamicSchemaIndex = schemaIndex - uint8(fieldLayout.numStaticFields()); + uint8 dynamicSchemaIndex = fieldIndex - uint8(fieldLayout.numStaticFields()); + + // Load dynamic data length from storage + uint256 dynamicSchemaLengthSlot = _getDynamicDataLengthLocation(tableId, keyTuple); + PackedCounter encodedLengths = PackedCounter.wrap(Storage.load({ storagePointer: dynamicSchemaLengthSlot })); + + // Update the encoded length + uint256 oldFieldLength = encodedLengths.atIndex(dynamicSchemaIndex); + encodedLengths = encodedLengths.setAtIndex(dynamicSchemaIndex, data.length); - // Update the dynamic data length - _setDynamicDataLengthAtIndex(tableId, keyTuple, dynamicSchemaIndex, data.length); + // Set the new lengths + Storage.store({ storagePointer: dynamicSchemaLengthSlot, data: encodedLengths.unwrap() }); // Store the provided value in storage uint256 dynamicDataLocation = _getDynamicDataLocation(tableId, keyTuple, dynamicSchemaIndex); Storage.store({ storagePointer: dynamicDataLocation, offset: 0, data: data }); + + // Compute start index for the splice event + uint256 start; + unchecked { + // (safe because it's a few uint40 values, which can't overflow uint48) + for (uint8 i; i < dynamicSchemaIndex; i++) { + start += encodedLengths.atIndex(i); + } + } + + // Emit event to notify indexers + emit StoreCore.StoreSpliceDynamicData({ + tableId: tableId, + keyTuple: keyTuple, + start: uint48(start), + deleteCount: uint40(oldFieldLength), + data: data, + encodedLengths: encodedLengths.unwrap() + }); } function _pushToDynamicField( bytes32 tableId, bytes32[] memory keyTuple, FieldLayout fieldLayout, - uint8 schemaIndex, + uint8 fieldIndex, bytes memory dataToPush ) internal { - uint8 dynamicSchemaIndex = schemaIndex - uint8(fieldLayout.numStaticFields()); + uint8 dynamicSchemaIndex = fieldIndex - uint8(fieldLayout.numStaticFields()); // Load dynamic data length from storage uint256 dynamicDataLengthSlot = _getDynamicDataLengthLocation(tableId, keyTuple); @@ -651,16 +766,35 @@ library StoreCoreInternal { // Append `dataToPush` to the end of the data in storage _setPartialDynamicData(tableId, keyTuple, dynamicSchemaIndex, oldFieldLength, dataToPush); + + // Compute start index for the splice event + uint256 start = oldFieldLength; + unchecked { + // (safe because it's a few uint40 values, which can't overflow uint48) + for (uint8 i; i < dynamicSchemaIndex; i++) { + start += encodedLengths.atIndex(i); + } + } + + // Emit event to notify indexers + emit StoreCore.StoreSpliceDynamicData({ + tableId: tableId, + keyTuple: keyTuple, + start: uint48(start), + deleteCount: uint40(0), + data: dataToPush, + encodedLengths: encodedLengths.unwrap() + }); } function _popFromDynamicField( bytes32 tableId, bytes32[] memory keyTuple, FieldLayout fieldLayout, - uint8 schemaIndex, + uint8 fieldIndex, uint256 byteLengthToPop ) internal { - uint8 dynamicSchemaIndex = schemaIndex - uint8(fieldLayout.numStaticFields()); + uint8 dynamicSchemaIndex = fieldIndex - uint8(fieldLayout.numStaticFields()); // Load dynamic data length from storage uint256 dynamicDataLengthSlot = _getDynamicDataLengthLocation(tableId, keyTuple); @@ -674,6 +808,27 @@ library StoreCoreInternal { Storage.store({ storagePointer: dynamicDataLengthSlot, data: encodedLengths.unwrap() }); // Data can be left unchanged, push/set do not assume storage to be 0s + + // Compute start index for the splice event + uint256 start; + unchecked { + // (safe because it's a few uint40 values, which can't overflow uint48) + start = oldFieldLength; + for (uint8 i; i < dynamicSchemaIndex; i++) { + start += encodedLengths.atIndex(i); + } + start -= byteLengthToPop; + } + + // Emit event to notify indexers + emit StoreCore.StoreSpliceDynamicData({ + tableId: tableId, + keyTuple: keyTuple, + start: uint48(start), + deleteCount: uint40(byteLengthToPop), + data: new bytes(0), + encodedLengths: encodedLengths.unwrap() + }); } // startOffset is measured in bytes @@ -681,14 +836,38 @@ library StoreCoreInternal { bytes32 tableId, bytes32[] memory keyTuple, FieldLayout fieldLayout, - uint8 schemaIndex, + uint8 fieldIndex, uint256 startByteIndex, bytes memory dataToSet ) internal { - uint8 dynamicSchemaIndex = schemaIndex - uint8(fieldLayout.numStaticFields()); + uint8 dynamicSchemaIndex = fieldIndex - uint8(fieldLayout.numStaticFields()); + + // Load dynamic data length from storage + uint256 dynamicSchemaLengthSlot = _getDynamicDataLengthLocation(tableId, keyTuple); + PackedCounter encodedLengths = PackedCounter.wrap(Storage.load({ storagePointer: dynamicSchemaLengthSlot })); // Set `dataToSet` at the given index _setPartialDynamicData(tableId, keyTuple, dynamicSchemaIndex, startByteIndex, dataToSet); + + // Compute start index for the splice event + uint256 start; + unchecked { + // (safe because it's a few uint40 values, which can't overflow uint48) + start = startByteIndex; + for (uint8 i; i < dynamicSchemaIndex; i++) { + start += encodedLengths.atIndex(i); + } + } + + // Emit event to notify indexers + emit StoreCore.StoreSpliceDynamicData({ + tableId: tableId, + keyTuple: keyTuple, + start: uint48(start), + deleteCount: uint40(dataToSet.length), + data: dataToSet, + encodedLengths: encodedLengths.unwrap() + }); } /************************************************************************ @@ -714,39 +893,23 @@ library StoreCoreInternal { } /** - * Get a single static field from the given tableId and key tuple, with the given value field layout + * Get a single static field from the given tableId and key tuple, with the given value field layout. + * Returns dynamic bytes memory in the size of the field. */ - function _getStaticField( + function _getStaticFieldBytes( bytes32 tableId, bytes32[] memory keyTuple, - uint8 schemaIndex, + uint8 fieldIndex, FieldLayout fieldLayout ) internal view returns (bytes memory) { // Get the length, storage location and offset of the static field - uint256 staticByteLength = fieldLayout.atIndex(schemaIndex); - uint256 location = _getStaticDataLocation(tableId, keyTuple); - uint256 offset = _getStaticDataOffset(fieldLayout, schemaIndex); - - // Load the data from storage - - return Storage.load({ storagePointer: location, length: staticByteLength, offset: offset }); - } - - /** - * Get a single dynamic field from the given tableId and key tuple, with the given value field layout - */ - function _getDynamicField( - bytes32 tableId, - bytes32[] memory keyTuple, - uint8 schemaIndex, - FieldLayout fieldLayout - ) internal view returns (bytes memory) { - // Get the length and storage location of the dynamic field - uint8 dynamicSchemaIndex = schemaIndex - uint8(fieldLayout.numStaticFields()); - uint256 location = _getDynamicDataLocation(tableId, keyTuple, dynamicSchemaIndex); - uint256 dataLength = _loadEncodedDynamicDataLength(tableId, keyTuple).atIndex(dynamicSchemaIndex); - - return Storage.load({ storagePointer: location, length: dataLength, offset: 0 }); + // and load the data from storage + return + Storage.load({ + storagePointer: StoreCoreInternal._getStaticDataLocation(tableId, keyTuple), + length: fieldLayout.atIndex(fieldIndex), + offset: StoreCoreInternal._getStaticDataOffset(fieldLayout, fieldIndex) + }); } /************************************************************************ @@ -761,19 +924,15 @@ library StoreCoreInternal { */ function _validateDataLength( FieldLayout fieldLayout, - bytes memory data - ) internal pure returns (uint256 staticLength, PackedCounter dynamicLength) { - staticLength = fieldLayout.staticDataLength(); - uint256 expectedLength = staticLength; - dynamicLength; - if (fieldLayout.numDynamicFields() > 0) { - // Dynamic length is encoded at the start of the dynamic length blob - dynamicLength = PackedCounter.wrap(Bytes.slice32(data, staticLength)); - expectedLength += 32 + dynamicLength.total(); // encoded length + data + bytes memory staticData, + PackedCounter encodedLengths, + bytes memory dynamicData + ) internal pure { + if (fieldLayout.staticDataLength() != staticData.length) { + revert IStoreErrors.StoreCore_InvalidStaticDataLength(fieldLayout.staticDataLength(), staticData.length); } - - if (expectedLength != data.length) { - revert IStoreErrors.StoreCore_InvalidDataLength(expectedLength, data.length); + if (encodedLengths.total() != dynamicData.length) { + revert IStoreErrors.StoreCore_InvalidDynamicDataLength(encodedLengths.total(), dynamicData.length); } } @@ -785,15 +944,15 @@ library StoreCoreInternal { * Compute the storage location based on tableId id and index tuple */ function _getStaticDataLocation(bytes32 tableId, bytes32[] memory keyTuple) internal pure returns (uint256) { - return uint256(keccak256(abi.encode(SLOT, tableId, keyTuple))); + return uint256(SLOT ^ keccak256(abi.encodePacked(tableId, keyTuple))); } /** * Get storage offset for the given value field layout and (static length) index */ - function _getStaticDataOffset(FieldLayout fieldLayout, uint8 schemaIndex) internal pure returns (uint256) { + function _getStaticDataOffset(FieldLayout fieldLayout, uint8 fieldIndex) internal pure returns (uint256) { uint256 offset = 0; - for (uint256 i; i < schemaIndex; i++) { + for (uint256 i; i < fieldIndex; i++) { offset += fieldLayout.atIndex(i); } return offset; @@ -809,16 +968,16 @@ library StoreCoreInternal { function _getDynamicDataLocation( bytes32 tableId, bytes32[] memory keyTuple, - uint8 schemaIndex + uint8 fieldIndex ) internal pure returns (uint256) { - return uint256(keccak256(abi.encode(SLOT, tableId, keyTuple, schemaIndex))); + return uint256(DYNMAIC_DATA_SLOT ^ bytes1(fieldIndex) ^ keccak256(abi.encodePacked(tableId, keyTuple))); } /** * Compute the storage location for the length of the dynamic data */ function _getDynamicDataLengthLocation(bytes32 tableId, bytes32[] memory keyTuple) internal pure returns (uint256) { - return uint256(keccak256(abi.encode(SLOT, tableId, keyTuple, "length"))); + return uint256(DYNAMIC_DATA_LENGTH_SLOT ^ keccak256(abi.encodePacked(tableId, keyTuple))); } /** @@ -839,7 +998,7 @@ library StoreCoreInternal { function _setDynamicDataLengthAtIndex( bytes32 tableId, bytes32[] memory keyTuple, - uint8 dynamicSchemaIndex, // schemaIndex - numStaticFields + uint8 dynamicSchemaIndex, // fieldIndex - numStaticFields uint256 newLengthAtIndex ) internal { // Load dynamic data length from storage @@ -866,6 +1025,7 @@ library StoreCoreInternal { uint256 dynamicDataLocation = _getDynamicDataLocation(tableId, keyTuple, dynamicSchemaIndex); // start index is in bytes, whereas storage slots are in 32-byte words dynamicDataLocation += startByteIndex / 32; + // partial storage slot offset (there is no inherent offset, as each dynamic field starts at its own storage slot) uint256 offset = startByteIndex % 32; Storage.store({ storagePointer: dynamicDataLocation, offset: offset, data: partialData }); diff --git a/packages/store/src/StoreRead.sol b/packages/store/src/StoreRead.sol index ced24eb297..3017f1510f 100644 --- a/packages/store/src/StoreRead.sol +++ b/packages/store/src/StoreRead.sol @@ -19,7 +19,6 @@ contract StoreRead is IStoreRead { keySchema = StoreCore.getKeySchema(tableId); } - // Get full record (static and dynamic data) function getRecord( bytes32 tableId, bytes32[] calldata keyTuple, @@ -28,33 +27,49 @@ contract StoreRead is IStoreRead { data = StoreCore.getRecord(tableId, keyTuple, fieldLayout); } - // Get partial data at schema index function getField( bytes32 tableId, bytes32[] calldata keyTuple, - uint8 schemaIndex, + uint8 fieldIndex, FieldLayout fieldLayout ) public view virtual returns (bytes memory data) { - data = StoreCore.getField(tableId, keyTuple, schemaIndex, fieldLayout); + data = StoreCore.getField(tableId, keyTuple, fieldIndex, fieldLayout); + } + + function getStaticField( + bytes32 tableId, + bytes32[] calldata keyTuple, + uint8 fieldIndex, + FieldLayout fieldLayout + ) public view virtual returns (bytes32 data) { + data = StoreCore.getStaticField(tableId, keyTuple, fieldIndex, fieldLayout); + } + + function getDynamicField( + bytes32 tableId, + bytes32[] calldata keyTuple, + uint8 dynamicFieldIndex + ) public view virtual returns (bytes memory data) { + data = StoreCore.getDynamicField(tableId, keyTuple, dynamicFieldIndex); } function getFieldLength( bytes32 tableId, bytes32[] memory keyTuple, - uint8 schemaIndex, + uint8 fieldIndex, FieldLayout fieldLayout ) public view virtual returns (uint256) { - return StoreCore.getFieldLength(tableId, keyTuple, schemaIndex, fieldLayout); + return StoreCore.getFieldLength(tableId, keyTuple, fieldIndex, fieldLayout); } function getFieldSlice( bytes32 tableId, bytes32[] memory keyTuple, - uint8 schemaIndex, + uint8 fieldIndex, FieldLayout fieldLayout, uint256 start, uint256 end ) public view virtual returns (bytes memory) { - return StoreCore.getFieldSlice(tableId, keyTuple, schemaIndex, fieldLayout, start, end); + return StoreCore.getFieldSlice(tableId, keyTuple, fieldIndex, fieldLayout, start, end); } } diff --git a/packages/store/src/StoreSwitch.sol b/packages/store/src/StoreSwitch.sol index 86aa1e311d..412d34bc5b 100644 --- a/packages/store/src/StoreSwitch.sol +++ b/packages/store/src/StoreSwitch.sol @@ -2,6 +2,7 @@ pragma solidity >=0.8.0; import { IStore } from "./IStore.sol"; +import { PackedCounter } from "../src/PackedCounter.sol"; import { IStoreHook } from "./IStoreHook.sol"; import { StoreCore } from "./StoreCore.sol"; import { Schema } from "./Schema.sol"; @@ -107,12 +108,19 @@ library StoreSwitch { } } - function setRecord(bytes32 tableId, bytes32[] memory keyTuple, bytes memory data, FieldLayout fieldLayout) internal { + function setRecord( + bytes32 tableId, + bytes32[] memory keyTuple, + bytes memory staticData, + PackedCounter encodedLengths, + bytes memory dynamicData, + FieldLayout fieldLayout + ) internal { address _storeAddress = getStoreAddress(); if (_storeAddress == address(this)) { - StoreCore.setRecord(tableId, keyTuple, data, fieldLayout); + StoreCore.setRecord(tableId, keyTuple, staticData, encodedLengths, dynamicData, fieldLayout); } else { - IStore(_storeAddress).setRecord(tableId, keyTuple, data, fieldLayout); + IStore(_storeAddress).setRecord(tableId, keyTuple, staticData, encodedLengths, dynamicData, fieldLayout); } } @@ -189,14 +197,23 @@ library StoreSwitch { function emitEphemeralRecord( bytes32 tableId, bytes32[] memory keyTuple, - bytes memory data, + bytes memory staticData, + PackedCounter encodedLengths, + bytes memory dynamicData, FieldLayout fieldLayout ) internal { address _storeAddress = getStoreAddress(); if (_storeAddress == address(this)) { - StoreCore.emitEphemeralRecord(tableId, keyTuple, data, fieldLayout); + StoreCore.emitEphemeralRecord(tableId, keyTuple, staticData, encodedLengths, dynamicData, fieldLayout); } else { - IStore(_storeAddress).emitEphemeralRecord(tableId, keyTuple, data, fieldLayout); + IStore(_storeAddress).emitEphemeralRecord( + tableId, + keyTuple, + staticData, + encodedLengths, + dynamicData, + fieldLayout + ); } } @@ -227,6 +244,33 @@ library StoreSwitch { } } + function getStaticField( + bytes32 tableId, + bytes32[] memory keyTuple, + uint8 fieldIndex, + FieldLayout fieldLayout + ) internal view returns (bytes32) { + address _storeAddress = getStoreAddress(); + if (_storeAddress == address(this)) { + return StoreCore.getStaticField(tableId, keyTuple, fieldIndex, fieldLayout); + } else { + return IStore(_storeAddress).getStaticField(tableId, keyTuple, fieldIndex, fieldLayout); + } + } + + function getDynamicField( + bytes32 tableId, + bytes32[] memory keyTuple, + uint8 dynamicFieldIndex + ) internal view returns (bytes memory) { + address _storeAddress = getStoreAddress(); + if (_storeAddress == address(this)) { + return StoreCore.getDynamicField(tableId, keyTuple, dynamicFieldIndex); + } else { + return IStore(_storeAddress).getDynamicField(tableId, keyTuple, dynamicFieldIndex); + } + } + function getFieldLength( bytes32 tableId, bytes32[] memory keyTuple, diff --git a/packages/store/src/codegen/tables/Callbacks.sol b/packages/store/src/codegen/tables/Callbacks.sol index 0126d86c18..499daf1a2a 100644 --- a/packages/store/src/codegen/tables/Callbacks.sol +++ b/packages/store/src/codegen/tables/Callbacks.sol @@ -21,12 +21,14 @@ import { PackedCounter, PackedCounterLib } from "../../PackedCounter.sol"; bytes32 constant _tableId = bytes32(abi.encodePacked(bytes16("mudstore"), bytes16("Callbacks"))); bytes32 constant CallbacksTableId = _tableId; +FieldLayout constant _fieldLayout = FieldLayout.wrap( + 0x0000000100000000000000000000000000000000000000000000000000000000 +); + library Callbacks { /** Get the table values' field layout */ function getFieldLayout() internal pure returns (FieldLayout) { - uint256[] memory _fieldLayout = new uint256[](0); - - return FieldLayoutLib.encode(_fieldLayout, 1); + return _fieldLayout; } /** Get the table's key schema */ @@ -59,19 +61,17 @@ library Callbacks { /** Register the table with its config */ function register() internal { - StoreSwitch.registerTable( - _tableId, - getFieldLayout(), - getKeySchema(), - getValueSchema(), - getKeyNames(), - getFieldNames() - ); + StoreSwitch.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + } + + /** Register the table with its config */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Register the table with its config (using the specified store) */ function register(IStore _store) internal { - _store.registerTable(_tableId, getFieldLayout(), getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + _store.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Get value */ @@ -79,7 +79,16 @@ library Callbacks { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 0, getFieldLayout()); + bytes memory _blob = StoreSwitch.getDynamicField(_tableId, _keyTuple, 0); + return (SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_bytes24()); + } + + /** Get value */ + function _get(bytes32 key) internal view returns (bytes24[] memory value) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + bytes memory _blob = StoreCore.getDynamicField(_tableId, _keyTuple, 0); return (SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_bytes24()); } @@ -88,7 +97,7 @@ library Callbacks { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = _store.getField(_tableId, _keyTuple, 0, getFieldLayout()); + bytes memory _blob = _store.getDynamicField(_tableId, _keyTuple, 0); return (SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_bytes24()); } @@ -97,7 +106,15 @@ library Callbacks { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.setField(_tableId, _keyTuple, 0, EncodeArray.encode((value)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 0, EncodeArray.encode((value)), _fieldLayout); + } + + /** Set value */ + function _set(bytes32 key, bytes24[] memory value) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.setField(_tableId, _keyTuple, 0, EncodeArray.encode((value)), _fieldLayout); } /** Set value (using the specified store) */ @@ -105,7 +122,7 @@ library Callbacks { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.setField(_tableId, _keyTuple, 0, EncodeArray.encode((value)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 0, EncodeArray.encode((value)), _fieldLayout); } /** Get the length of value */ @@ -113,7 +130,18 @@ library Callbacks { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 0, getFieldLayout()); + uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 0, _fieldLayout); + unchecked { + return _byteLength / 24; + } + } + + /** Get the length of value */ + function _length(bytes32 key) internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + uint256 _byteLength = StoreCore.getFieldLength(_tableId, _keyTuple, 0, _fieldLayout); unchecked { return _byteLength / 24; } @@ -124,7 +152,7 @@ library Callbacks { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 0, getFieldLayout()); + uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 0, _fieldLayout); unchecked { return _byteLength / 24; } @@ -143,32 +171,46 @@ library Callbacks { _tableId, _keyTuple, 0, - getFieldLayout(), + _fieldLayout, _index * 24, (_index + 1) * 24 ); - return (Bytes.slice24(_blob, 0)); + return (bytes24(_blob)); } } /** - * Get an item of value (using the specified store) + * Get an item of value * (unchecked, returns invalid data if index overflows) */ - function getItem(IStore _store, bytes32 key, uint256 _index) internal view returns (bytes24) { + function _getItem(bytes32 key, uint256 _index) internal view returns (bytes24) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; unchecked { - bytes memory _blob = _store.getFieldSlice( + bytes memory _blob = StoreCore.getFieldSlice( _tableId, _keyTuple, 0, - getFieldLayout(), + _fieldLayout, _index * 24, (_index + 1) * 24 ); - return (Bytes.slice24(_blob, 0)); + return (bytes24(_blob)); + } + } + + /** + * Get an item of value (using the specified store) + * (unchecked, returns invalid data if index overflows) + */ + function getItem(IStore _store, bytes32 key, uint256 _index) internal view returns (bytes24) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + unchecked { + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 0, _fieldLayout, _index * 24, (_index + 1) * 24); + return (bytes24(_blob)); } } @@ -177,7 +219,15 @@ library Callbacks { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), getFieldLayout()); + StoreSwitch.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), _fieldLayout); + } + + /** Push an element to value */ + function _push(bytes32 key, bytes24 _element) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), _fieldLayout); } /** Push an element to value (using the specified store) */ @@ -185,7 +235,7 @@ library Callbacks { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), getFieldLayout()); + _store.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), _fieldLayout); } /** Pop an element from value */ @@ -193,7 +243,15 @@ library Callbacks { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.popFromField(_tableId, _keyTuple, 0, 24, getFieldLayout()); + StoreSwitch.popFromField(_tableId, _keyTuple, 0, 24, _fieldLayout); + } + + /** Pop an element from value */ + function _pop(bytes32 key) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.popFromField(_tableId, _keyTuple, 0, 24, _fieldLayout); } /** Pop an element from value (using the specified store) */ @@ -201,7 +259,7 @@ library Callbacks { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.popFromField(_tableId, _keyTuple, 0, 24, getFieldLayout()); + _store.popFromField(_tableId, _keyTuple, 0, 24, _fieldLayout); } /** @@ -213,7 +271,20 @@ library Callbacks { _keyTuple[0] = key; unchecked { - StoreSwitch.updateInField(_tableId, _keyTuple, 0, _index * 24, abi.encodePacked((_element)), getFieldLayout()); + StoreSwitch.updateInField(_tableId, _keyTuple, 0, _index * 24, abi.encodePacked((_element)), _fieldLayout); + } + } + + /** + * Update an element of value at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ + function _update(bytes32 key, uint256 _index, bytes24 _element) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + unchecked { + StoreCore.updateInField(_tableId, _keyTuple, 0, _index * 24, abi.encodePacked((_element)), _fieldLayout); } } @@ -226,19 +297,30 @@ library Callbacks { _keyTuple[0] = key; unchecked { - _store.updateInField(_tableId, _keyTuple, 0, _index * 24, abi.encodePacked((_element)), getFieldLayout()); + _store.updateInField(_tableId, _keyTuple, 0, _index * 24, abi.encodePacked((_element)), _fieldLayout); } } - /** Tightly pack full data using this table's field layout */ - function encode(bytes24[] memory value) internal pure returns (bytes memory) { - PackedCounter _encodedLengths; + /** Tightly pack dynamic data using this table's schema */ + function encodeLengths(bytes24[] memory value) internal pure returns (PackedCounter _encodedLengths) { // Lengths are effectively checked during copy by 2**40 bytes exceeding gas limits unchecked { _encodedLengths = PackedCounterLib.pack(value.length * 24); } + } - return abi.encodePacked(_encodedLengths.unwrap(), EncodeArray.encode((value))); + /** Tightly pack dynamic data using this table's schema */ + function encodeDynamic(bytes24[] memory value) internal pure returns (bytes memory) { + return abi.encodePacked(EncodeArray.encode((value))); + } + + /** Tightly pack full data using this table's field layout */ + function encode(bytes24[] memory value) internal pure returns (bytes memory) { + bytes memory _staticData; + PackedCounter _encodedLengths = encodeLengths(value); + bytes memory _dynamicData = encodeDynamic(value); + + return abi.encodePacked(_staticData, _encodedLengths, _dynamicData); } /** Encode keys as a bytes32 array using this table's field layout */ @@ -254,7 +336,15 @@ library Callbacks { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + StoreSwitch.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /* Delete all data for given keys */ + function _deleteRecord(bytes32 key) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); } /* Delete all data for given keys (using the specified store) */ @@ -262,6 +352,6 @@ library Callbacks { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + _store.deleteRecord(_tableId, _keyTuple, _fieldLayout); } } diff --git a/packages/store/src/codegen/tables/Hooks.sol b/packages/store/src/codegen/tables/Hooks.sol index 242d5c9730..d5eaafb986 100644 --- a/packages/store/src/codegen/tables/Hooks.sol +++ b/packages/store/src/codegen/tables/Hooks.sol @@ -18,12 +18,14 @@ import { FieldLayout, FieldLayoutLib } from "../../FieldLayout.sol"; import { Schema, SchemaLib } from "../../Schema.sol"; import { PackedCounter, PackedCounterLib } from "../../PackedCounter.sol"; +FieldLayout constant _fieldLayout = FieldLayout.wrap( + 0x0000000100000000000000000000000000000000000000000000000000000000 +); + library Hooks { /** Get the table values' field layout */ function getFieldLayout() internal pure returns (FieldLayout) { - uint256[] memory _fieldLayout = new uint256[](0); - - return FieldLayoutLib.encode(_fieldLayout, 1); + return _fieldLayout; } /** Get the table's key schema */ @@ -56,19 +58,17 @@ library Hooks { /** Register the table with its config */ function register(bytes32 _tableId) internal { - StoreSwitch.registerTable( - _tableId, - getFieldLayout(), - getKeySchema(), - getValueSchema(), - getKeyNames(), - getFieldNames() - ); + StoreSwitch.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + } + + /** Register the table with its config */ + function _register(bytes32 _tableId) internal { + StoreCore.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Register the table with its config (using the specified store) */ function register(IStore _store, bytes32 _tableId) internal { - _store.registerTable(_tableId, getFieldLayout(), getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + _store.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Get value */ @@ -76,7 +76,16 @@ library Hooks { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 0, getFieldLayout()); + bytes memory _blob = StoreSwitch.getDynamicField(_tableId, _keyTuple, 0); + return (SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_bytes21()); + } + + /** Get value */ + function _get(bytes32 _tableId, bytes32 key) internal view returns (bytes21[] memory value) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + bytes memory _blob = StoreCore.getDynamicField(_tableId, _keyTuple, 0); return (SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_bytes21()); } @@ -85,7 +94,7 @@ library Hooks { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = _store.getField(_tableId, _keyTuple, 0, getFieldLayout()); + bytes memory _blob = _store.getDynamicField(_tableId, _keyTuple, 0); return (SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_bytes21()); } @@ -94,7 +103,15 @@ library Hooks { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.setField(_tableId, _keyTuple, 0, EncodeArray.encode((value)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 0, EncodeArray.encode((value)), _fieldLayout); + } + + /** Set value */ + function _set(bytes32 _tableId, bytes32 key, bytes21[] memory value) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.setField(_tableId, _keyTuple, 0, EncodeArray.encode((value)), _fieldLayout); } /** Set value (using the specified store) */ @@ -102,7 +119,7 @@ library Hooks { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.setField(_tableId, _keyTuple, 0, EncodeArray.encode((value)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 0, EncodeArray.encode((value)), _fieldLayout); } /** Get the length of value */ @@ -110,7 +127,18 @@ library Hooks { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 0, getFieldLayout()); + uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 0, _fieldLayout); + unchecked { + return _byteLength / 21; + } + } + + /** Get the length of value */ + function _length(bytes32 _tableId, bytes32 key) internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + uint256 _byteLength = StoreCore.getFieldLength(_tableId, _keyTuple, 0, _fieldLayout); unchecked { return _byteLength / 21; } @@ -121,7 +149,7 @@ library Hooks { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 0, getFieldLayout()); + uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 0, _fieldLayout); unchecked { return _byteLength / 21; } @@ -140,32 +168,46 @@ library Hooks { _tableId, _keyTuple, 0, - getFieldLayout(), + _fieldLayout, _index * 21, (_index + 1) * 21 ); - return (Bytes.slice21(_blob, 0)); + return (bytes21(_blob)); } } /** - * Get an item of value (using the specified store) + * Get an item of value * (unchecked, returns invalid data if index overflows) */ - function getItem(IStore _store, bytes32 _tableId, bytes32 key, uint256 _index) internal view returns (bytes21) { + function _getItem(bytes32 _tableId, bytes32 key, uint256 _index) internal view returns (bytes21) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; unchecked { - bytes memory _blob = _store.getFieldSlice( + bytes memory _blob = StoreCore.getFieldSlice( _tableId, _keyTuple, 0, - getFieldLayout(), + _fieldLayout, _index * 21, (_index + 1) * 21 ); - return (Bytes.slice21(_blob, 0)); + return (bytes21(_blob)); + } + } + + /** + * Get an item of value (using the specified store) + * (unchecked, returns invalid data if index overflows) + */ + function getItem(IStore _store, bytes32 _tableId, bytes32 key, uint256 _index) internal view returns (bytes21) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + unchecked { + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 0, _fieldLayout, _index * 21, (_index + 1) * 21); + return (bytes21(_blob)); } } @@ -174,7 +216,15 @@ library Hooks { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), getFieldLayout()); + StoreSwitch.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), _fieldLayout); + } + + /** Push an element to value */ + function _push(bytes32 _tableId, bytes32 key, bytes21 _element) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), _fieldLayout); } /** Push an element to value (using the specified store) */ @@ -182,7 +232,7 @@ library Hooks { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), getFieldLayout()); + _store.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), _fieldLayout); } /** Pop an element from value */ @@ -190,7 +240,15 @@ library Hooks { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.popFromField(_tableId, _keyTuple, 0, 21, getFieldLayout()); + StoreSwitch.popFromField(_tableId, _keyTuple, 0, 21, _fieldLayout); + } + + /** Pop an element from value */ + function _pop(bytes32 _tableId, bytes32 key) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.popFromField(_tableId, _keyTuple, 0, 21, _fieldLayout); } /** Pop an element from value (using the specified store) */ @@ -198,7 +256,7 @@ library Hooks { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.popFromField(_tableId, _keyTuple, 0, 21, getFieldLayout()); + _store.popFromField(_tableId, _keyTuple, 0, 21, _fieldLayout); } /** @@ -210,7 +268,20 @@ library Hooks { _keyTuple[0] = key; unchecked { - StoreSwitch.updateInField(_tableId, _keyTuple, 0, _index * 21, abi.encodePacked((_element)), getFieldLayout()); + StoreSwitch.updateInField(_tableId, _keyTuple, 0, _index * 21, abi.encodePacked((_element)), _fieldLayout); + } + } + + /** + * Update an element of value at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ + function _update(bytes32 _tableId, bytes32 key, uint256 _index, bytes21 _element) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + unchecked { + StoreCore.updateInField(_tableId, _keyTuple, 0, _index * 21, abi.encodePacked((_element)), _fieldLayout); } } @@ -223,19 +294,30 @@ library Hooks { _keyTuple[0] = key; unchecked { - _store.updateInField(_tableId, _keyTuple, 0, _index * 21, abi.encodePacked((_element)), getFieldLayout()); + _store.updateInField(_tableId, _keyTuple, 0, _index * 21, abi.encodePacked((_element)), _fieldLayout); } } - /** Tightly pack full data using this table's field layout */ - function encode(bytes21[] memory value) internal pure returns (bytes memory) { - PackedCounter _encodedLengths; + /** Tightly pack dynamic data using this table's schema */ + function encodeLengths(bytes21[] memory value) internal pure returns (PackedCounter _encodedLengths) { // Lengths are effectively checked during copy by 2**40 bytes exceeding gas limits unchecked { _encodedLengths = PackedCounterLib.pack(value.length * 21); } + } - return abi.encodePacked(_encodedLengths.unwrap(), EncodeArray.encode((value))); + /** Tightly pack dynamic data using this table's schema */ + function encodeDynamic(bytes21[] memory value) internal pure returns (bytes memory) { + return abi.encodePacked(EncodeArray.encode((value))); + } + + /** Tightly pack full data using this table's field layout */ + function encode(bytes21[] memory value) internal pure returns (bytes memory) { + bytes memory _staticData; + PackedCounter _encodedLengths = encodeLengths(value); + bytes memory _dynamicData = encodeDynamic(value); + + return abi.encodePacked(_staticData, _encodedLengths, _dynamicData); } /** Encode keys as a bytes32 array using this table's field layout */ @@ -251,7 +333,15 @@ library Hooks { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + StoreSwitch.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /* Delete all data for given keys */ + function _deleteRecord(bytes32 _tableId, bytes32 key) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); } /* Delete all data for given keys (using the specified store) */ @@ -259,6 +349,6 @@ library Hooks { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + _store.deleteRecord(_tableId, _keyTuple, _fieldLayout); } } diff --git a/packages/store/src/codegen/tables/KeyEncoding.sol b/packages/store/src/codegen/tables/KeyEncoding.sol index f2344cf382..47cc468cb7 100644 --- a/packages/store/src/codegen/tables/KeyEncoding.sol +++ b/packages/store/src/codegen/tables/KeyEncoding.sol @@ -24,13 +24,14 @@ import { ExampleEnum } from "./../Types.sol"; bytes32 constant _tableId = bytes32(abi.encodePacked(bytes16("mudstore"), bytes16("KeyEncoding"))); bytes32 constant KeyEncodingTableId = _tableId; +FieldLayout constant _fieldLayout = FieldLayout.wrap( + 0x0001010001000000000000000000000000000000000000000000000000000000 +); + library KeyEncoding { /** Get the table values' field layout */ function getFieldLayout() internal pure returns (FieldLayout) { - uint256[] memory _fieldLayout = new uint256[](1); - _fieldLayout[0] = 1; - - return FieldLayoutLib.encode(_fieldLayout, 0); + return _fieldLayout; } /** Get the table's key schema */ @@ -73,19 +74,17 @@ library KeyEncoding { /** Register the table with its config */ function register() internal { - StoreSwitch.registerTable( - _tableId, - getFieldLayout(), - getKeySchema(), - getValueSchema(), - getKeyNames(), - getFieldNames() - ); + StoreSwitch.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + } + + /** Register the table with its config */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Register the table with its config (using the specified store) */ function register(IStore _store) internal { - _store.registerTable(_tableId, getFieldLayout(), getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + _store.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Get value */ @@ -105,8 +104,29 @@ library KeyEncoding { _keyTuple[4] = _boolToBytes32(k5); _keyTuple[5] = bytes32(uint256(uint8(k6))); - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (_toBool(uint8(Bytes.slice1(_blob, 0)))); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (_toBool(uint8(bytes1(_blob)))); + } + + /** Get value */ + function _get( + uint256 k1, + int32 k2, + bytes16 k3, + address k4, + bool k5, + ExampleEnum k6 + ) internal view returns (bool value) { + bytes32[] memory _keyTuple = new bytes32[](6); + _keyTuple[0] = bytes32(uint256(k1)); + _keyTuple[1] = bytes32(uint256(int256(k2))); + _keyTuple[2] = bytes32(k3); + _keyTuple[3] = bytes32(uint256(uint160(k4))); + _keyTuple[4] = _boolToBytes32(k5); + _keyTuple[5] = bytes32(uint256(uint8(k6))); + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (_toBool(uint8(bytes1(_blob)))); } /** Get value (using the specified store) */ @@ -127,8 +147,8 @@ library KeyEncoding { _keyTuple[4] = _boolToBytes32(k5); _keyTuple[5] = bytes32(uint256(uint8(k6))); - bytes memory _blob = _store.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (_toBool(uint8(Bytes.slice1(_blob, 0)))); + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (_toBool(uint8(bytes1(_blob)))); } /** Set value */ @@ -141,7 +161,20 @@ library KeyEncoding { _keyTuple[4] = _boolToBytes32(k5); _keyTuple[5] = bytes32(uint256(uint8(k6))); - StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((value)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); + } + + /** Set value */ + function _set(uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, ExampleEnum k6, bool value) internal { + bytes32[] memory _keyTuple = new bytes32[](6); + _keyTuple[0] = bytes32(uint256(k1)); + _keyTuple[1] = bytes32(uint256(int256(k2))); + _keyTuple[2] = bytes32(k3); + _keyTuple[3] = bytes32(uint256(uint160(k4))); + _keyTuple[4] = _boolToBytes32(k5); + _keyTuple[5] = bytes32(uint256(uint8(k6))); + + StoreCore.setField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); } /** Set value (using the specified store) */ @@ -163,12 +196,22 @@ library KeyEncoding { _keyTuple[4] = _boolToBytes32(k5); _keyTuple[5] = bytes32(uint256(uint8(k6))); - _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((value)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); + } + + /** Tightly pack static data using this table's schema */ + function encodeStatic(bool value) internal pure returns (bytes memory) { + return abi.encodePacked(value); } /** Tightly pack full data using this table's field layout */ function encode(bool value) internal pure returns (bytes memory) { - return abi.encodePacked(value); + bytes memory _staticData = encodeStatic(value); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; + + return abi.encodePacked(_staticData, _encodedLengths, _dynamicData); } /** Encode keys as a bytes32 array using this table's field layout */ @@ -201,7 +244,20 @@ library KeyEncoding { _keyTuple[4] = _boolToBytes32(k5); _keyTuple[5] = bytes32(uint256(uint8(k6))); - StoreSwitch.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + StoreSwitch.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /* Delete all data for given keys */ + function _deleteRecord(uint256 k1, int32 k2, bytes16 k3, address k4, bool k5, ExampleEnum k6) internal { + bytes32[] memory _keyTuple = new bytes32[](6); + _keyTuple[0] = bytes32(uint256(k1)); + _keyTuple[1] = bytes32(uint256(int256(k2))); + _keyTuple[2] = bytes32(k3); + _keyTuple[3] = bytes32(uint256(uint160(k4))); + _keyTuple[4] = _boolToBytes32(k5); + _keyTuple[5] = bytes32(uint256(uint8(k6))); + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); } /* Delete all data for given keys (using the specified store) */ @@ -214,7 +270,7 @@ library KeyEncoding { _keyTuple[4] = _boolToBytes32(k5); _keyTuple[5] = bytes32(uint256(uint8(k6))); - _store.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + _store.deleteRecord(_tableId, _keyTuple, _fieldLayout); } } diff --git a/packages/store/src/codegen/tables/Mixed.sol b/packages/store/src/codegen/tables/Mixed.sol index 22f664c8e1..fbadcc31ee 100644 --- a/packages/store/src/codegen/tables/Mixed.sol +++ b/packages/store/src/codegen/tables/Mixed.sol @@ -21,6 +21,10 @@ import { PackedCounter, PackedCounterLib } from "../../PackedCounter.sol"; bytes32 constant _tableId = bytes32(abi.encodePacked(bytes16("mudstore"), bytes16("Mixed"))); bytes32 constant MixedTableId = _tableId; +FieldLayout constant _fieldLayout = FieldLayout.wrap( + 0x0014020204100000000000000000000000000000000000000000000000000000 +); + struct MixedData { uint32 u32; uint128 u128; @@ -31,11 +35,7 @@ struct MixedData { library Mixed { /** Get the table values' field layout */ function getFieldLayout() internal pure returns (FieldLayout) { - uint256[] memory _fieldLayout = new uint256[](2); - _fieldLayout[0] = 4; - _fieldLayout[1] = 16; - - return FieldLayoutLib.encode(_fieldLayout, 2); + return _fieldLayout; } /** Get the table's key schema */ @@ -74,19 +74,17 @@ library Mixed { /** Register the table with its config */ function register() internal { - StoreSwitch.registerTable( - _tableId, - getFieldLayout(), - getKeySchema(), - getValueSchema(), - getKeyNames(), - getFieldNames() - ); + StoreSwitch.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + } + + /** Register the table with its config */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Register the table with its config (using the specified store) */ function register(IStore _store) internal { - _store.registerTable(_tableId, getFieldLayout(), getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + _store.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Get u32 */ @@ -94,8 +92,17 @@ library Mixed { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (uint32(Bytes.slice4(_blob, 0))); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint32(bytes4(_blob))); + } + + /** Get u32 */ + function _getU32(bytes32 key) internal view returns (uint32 u32) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint32(bytes4(_blob))); } /** Get u32 (using the specified store) */ @@ -103,8 +110,8 @@ library Mixed { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = _store.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (uint32(Bytes.slice4(_blob, 0))); + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint32(bytes4(_blob))); } /** Set u32 */ @@ -112,7 +119,15 @@ library Mixed { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((u32)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((u32)), _fieldLayout); + } + + /** Set u32 */ + function _setU32(bytes32 key, uint32 u32) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.setField(_tableId, _keyTuple, 0, abi.encodePacked((u32)), _fieldLayout); } /** Set u32 (using the specified store) */ @@ -120,7 +135,7 @@ library Mixed { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((u32)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((u32)), _fieldLayout); } /** Get u128 */ @@ -128,8 +143,17 @@ library Mixed { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 1, getFieldLayout()); - return (uint128(Bytes.slice16(_blob, 0))); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 1, _fieldLayout); + return (uint128(bytes16(_blob))); + } + + /** Get u128 */ + function _getU128(bytes32 key) internal view returns (uint128 u128) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 1, _fieldLayout); + return (uint128(bytes16(_blob))); } /** Get u128 (using the specified store) */ @@ -137,8 +161,8 @@ library Mixed { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = _store.getField(_tableId, _keyTuple, 1, getFieldLayout()); - return (uint128(Bytes.slice16(_blob, 0))); + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 1, _fieldLayout); + return (uint128(bytes16(_blob))); } /** Set u128 */ @@ -146,7 +170,15 @@ library Mixed { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.setField(_tableId, _keyTuple, 1, abi.encodePacked((u128)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 1, abi.encodePacked((u128)), _fieldLayout); + } + + /** Set u128 */ + function _setU128(bytes32 key, uint128 u128) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.setField(_tableId, _keyTuple, 1, abi.encodePacked((u128)), _fieldLayout); } /** Set u128 (using the specified store) */ @@ -154,7 +186,7 @@ library Mixed { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.setField(_tableId, _keyTuple, 1, abi.encodePacked((u128)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 1, abi.encodePacked((u128)), _fieldLayout); } /** Get a32 */ @@ -162,7 +194,16 @@ library Mixed { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 2, getFieldLayout()); + bytes memory _blob = StoreSwitch.getDynamicField(_tableId, _keyTuple, 0); + return (SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_uint32()); + } + + /** Get a32 */ + function _getA32(bytes32 key) internal view returns (uint32[] memory a32) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + bytes memory _blob = StoreCore.getDynamicField(_tableId, _keyTuple, 0); return (SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_uint32()); } @@ -171,7 +212,7 @@ library Mixed { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = _store.getField(_tableId, _keyTuple, 2, getFieldLayout()); + bytes memory _blob = _store.getDynamicField(_tableId, _keyTuple, 0); return (SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_uint32()); } @@ -180,7 +221,15 @@ library Mixed { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.setField(_tableId, _keyTuple, 2, EncodeArray.encode((a32)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 2, EncodeArray.encode((a32)), _fieldLayout); + } + + /** Set a32 */ + function _setA32(bytes32 key, uint32[] memory a32) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.setField(_tableId, _keyTuple, 2, EncodeArray.encode((a32)), _fieldLayout); } /** Set a32 (using the specified store) */ @@ -188,7 +237,7 @@ library Mixed { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.setField(_tableId, _keyTuple, 2, EncodeArray.encode((a32)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 2, EncodeArray.encode((a32)), _fieldLayout); } /** Get the length of a32 */ @@ -196,7 +245,18 @@ library Mixed { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 2, getFieldLayout()); + uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 2, _fieldLayout); + unchecked { + return _byteLength / 4; + } + } + + /** Get the length of a32 */ + function _lengthA32(bytes32 key) internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + uint256 _byteLength = StoreCore.getFieldLength(_tableId, _keyTuple, 2, _fieldLayout); unchecked { return _byteLength / 4; } @@ -207,7 +267,7 @@ library Mixed { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 2, getFieldLayout()); + uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 2, _fieldLayout); unchecked { return _byteLength / 4; } @@ -226,11 +286,25 @@ library Mixed { _tableId, _keyTuple, 2, - getFieldLayout(), + _fieldLayout, _index * 4, (_index + 1) * 4 ); - return (uint32(Bytes.slice4(_blob, 0))); + return (uint32(bytes4(_blob))); + } + } + + /** + * Get an item of a32 + * (unchecked, returns invalid data if index overflows) + */ + function _getItemA32(bytes32 key, uint256 _index) internal view returns (uint32) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + unchecked { + bytes memory _blob = StoreCore.getFieldSlice(_tableId, _keyTuple, 2, _fieldLayout, _index * 4, (_index + 1) * 4); + return (uint32(bytes4(_blob))); } } @@ -243,8 +317,8 @@ library Mixed { _keyTuple[0] = key; unchecked { - bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 2, getFieldLayout(), _index * 4, (_index + 1) * 4); - return (uint32(Bytes.slice4(_blob, 0))); + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 2, _fieldLayout, _index * 4, (_index + 1) * 4); + return (uint32(bytes4(_blob))); } } @@ -253,7 +327,15 @@ library Mixed { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.pushToField(_tableId, _keyTuple, 2, abi.encodePacked((_element)), getFieldLayout()); + StoreSwitch.pushToField(_tableId, _keyTuple, 2, abi.encodePacked((_element)), _fieldLayout); + } + + /** Push an element to a32 */ + function _pushA32(bytes32 key, uint32 _element) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.pushToField(_tableId, _keyTuple, 2, abi.encodePacked((_element)), _fieldLayout); } /** Push an element to a32 (using the specified store) */ @@ -261,7 +343,7 @@ library Mixed { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.pushToField(_tableId, _keyTuple, 2, abi.encodePacked((_element)), getFieldLayout()); + _store.pushToField(_tableId, _keyTuple, 2, abi.encodePacked((_element)), _fieldLayout); } /** Pop an element from a32 */ @@ -269,7 +351,15 @@ library Mixed { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.popFromField(_tableId, _keyTuple, 2, 4, getFieldLayout()); + StoreSwitch.popFromField(_tableId, _keyTuple, 2, 4, _fieldLayout); + } + + /** Pop an element from a32 */ + function _popA32(bytes32 key) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.popFromField(_tableId, _keyTuple, 2, 4, _fieldLayout); } /** Pop an element from a32 (using the specified store) */ @@ -277,7 +367,7 @@ library Mixed { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.popFromField(_tableId, _keyTuple, 2, 4, getFieldLayout()); + _store.popFromField(_tableId, _keyTuple, 2, 4, _fieldLayout); } /** @@ -289,7 +379,20 @@ library Mixed { _keyTuple[0] = key; unchecked { - StoreSwitch.updateInField(_tableId, _keyTuple, 2, _index * 4, abi.encodePacked((_element)), getFieldLayout()); + StoreSwitch.updateInField(_tableId, _keyTuple, 2, _index * 4, abi.encodePacked((_element)), _fieldLayout); + } + } + + /** + * Update an element of a32 at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ + function _updateA32(bytes32 key, uint256 _index, uint32 _element) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + unchecked { + StoreCore.updateInField(_tableId, _keyTuple, 2, _index * 4, abi.encodePacked((_element)), _fieldLayout); } } @@ -302,7 +405,7 @@ library Mixed { _keyTuple[0] = key; unchecked { - _store.updateInField(_tableId, _keyTuple, 2, _index * 4, abi.encodePacked((_element)), getFieldLayout()); + _store.updateInField(_tableId, _keyTuple, 2, _index * 4, abi.encodePacked((_element)), _fieldLayout); } } @@ -311,7 +414,16 @@ library Mixed { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 3, getFieldLayout()); + bytes memory _blob = StoreSwitch.getDynamicField(_tableId, _keyTuple, 1); + return (string(_blob)); + } + + /** Get s */ + function _getS(bytes32 key) internal view returns (string memory s) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + bytes memory _blob = StoreCore.getDynamicField(_tableId, _keyTuple, 1); return (string(_blob)); } @@ -320,7 +432,7 @@ library Mixed { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = _store.getField(_tableId, _keyTuple, 3, getFieldLayout()); + bytes memory _blob = _store.getDynamicField(_tableId, _keyTuple, 1); return (string(_blob)); } @@ -329,7 +441,15 @@ library Mixed { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.setField(_tableId, _keyTuple, 3, bytes((s)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 3, bytes((s)), _fieldLayout); + } + + /** Set s */ + function _setS(bytes32 key, string memory s) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.setField(_tableId, _keyTuple, 3, bytes((s)), _fieldLayout); } /** Set s (using the specified store) */ @@ -337,7 +457,7 @@ library Mixed { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.setField(_tableId, _keyTuple, 3, bytes((s)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 3, bytes((s)), _fieldLayout); } /** Get the length of s */ @@ -345,7 +465,18 @@ library Mixed { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 3, getFieldLayout()); + uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 3, _fieldLayout); + unchecked { + return _byteLength / 1; + } + } + + /** Get the length of s */ + function _lengthS(bytes32 key) internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + uint256 _byteLength = StoreCore.getFieldLength(_tableId, _keyTuple, 3, _fieldLayout); unchecked { return _byteLength / 1; } @@ -356,7 +487,7 @@ library Mixed { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 3, getFieldLayout()); + uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 3, _fieldLayout); unchecked { return _byteLength / 1; } @@ -375,7 +506,7 @@ library Mixed { _tableId, _keyTuple, 3, - getFieldLayout(), + _fieldLayout, _index * 1, (_index + 1) * 1 ); @@ -383,6 +514,20 @@ library Mixed { } } + /** + * Get an item of s + * (unchecked, returns invalid data if index overflows) + */ + function _getItemS(bytes32 key, uint256 _index) internal view returns (string memory) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + unchecked { + bytes memory _blob = StoreCore.getFieldSlice(_tableId, _keyTuple, 3, _fieldLayout, _index * 1, (_index + 1) * 1); + return (string(_blob)); + } + } + /** * Get an item of s (using the specified store) * (unchecked, returns invalid data if index overflows) @@ -392,7 +537,7 @@ library Mixed { _keyTuple[0] = key; unchecked { - bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 3, getFieldLayout(), _index * 1, (_index + 1) * 1); + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 3, _fieldLayout, _index * 1, (_index + 1) * 1); return (string(_blob)); } } @@ -402,7 +547,15 @@ library Mixed { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.pushToField(_tableId, _keyTuple, 3, bytes((_slice)), getFieldLayout()); + StoreSwitch.pushToField(_tableId, _keyTuple, 3, bytes((_slice)), _fieldLayout); + } + + /** Push a slice to s */ + function _pushS(bytes32 key, string memory _slice) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.pushToField(_tableId, _keyTuple, 3, bytes((_slice)), _fieldLayout); } /** Push a slice to s (using the specified store) */ @@ -410,7 +563,7 @@ library Mixed { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.pushToField(_tableId, _keyTuple, 3, bytes((_slice)), getFieldLayout()); + _store.pushToField(_tableId, _keyTuple, 3, bytes((_slice)), _fieldLayout); } /** Pop a slice from s */ @@ -418,7 +571,15 @@ library Mixed { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.popFromField(_tableId, _keyTuple, 3, 1, getFieldLayout()); + StoreSwitch.popFromField(_tableId, _keyTuple, 3, 1, _fieldLayout); + } + + /** Pop a slice from s */ + function _popS(bytes32 key) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.popFromField(_tableId, _keyTuple, 3, 1, _fieldLayout); } /** Pop a slice from s (using the specified store) */ @@ -426,7 +587,7 @@ library Mixed { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.popFromField(_tableId, _keyTuple, 3, 1, getFieldLayout()); + _store.popFromField(_tableId, _keyTuple, 3, 1, _fieldLayout); } /** @@ -438,7 +599,20 @@ library Mixed { _keyTuple[0] = key; unchecked { - StoreSwitch.updateInField(_tableId, _keyTuple, 3, _index * 1, bytes((_slice)), getFieldLayout()); + StoreSwitch.updateInField(_tableId, _keyTuple, 3, _index * 1, bytes((_slice)), _fieldLayout); + } + } + + /** + * Update a slice of s at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ + function _updateS(bytes32 key, uint256 _index, string memory _slice) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + unchecked { + StoreCore.updateInField(_tableId, _keyTuple, 3, _index * 1, bytes((_slice)), _fieldLayout); } } @@ -451,7 +625,7 @@ library Mixed { _keyTuple[0] = key; unchecked { - _store.updateInField(_tableId, _keyTuple, 3, _index * 1, bytes((_slice)), getFieldLayout()); + _store.updateInField(_tableId, _keyTuple, 3, _index * 1, bytes((_slice)), _fieldLayout); } } @@ -460,7 +634,16 @@ library Mixed { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = StoreSwitch.getRecord(_tableId, _keyTuple, getFieldLayout()); + bytes memory _blob = StoreSwitch.getRecord(_tableId, _keyTuple, _fieldLayout); + return decode(_blob); + } + + /** Get the full data */ + function _get(bytes32 key) internal view returns (MixedData memory _table) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + bytes memory _blob = StoreCore.getRecord(_tableId, _keyTuple, _fieldLayout); return decode(_blob); } @@ -469,28 +652,47 @@ library Mixed { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = _store.getRecord(_tableId, _keyTuple, getFieldLayout()); + bytes memory _blob = _store.getRecord(_tableId, _keyTuple, _fieldLayout); return decode(_blob); } /** Set the full data using individual values */ function set(bytes32 key, uint32 u32, uint128 u128, uint32[] memory a32, string memory s) internal { - bytes memory _data = encode(u32, u128, a32, s); + bytes memory _staticData = encodeStatic(u32, u128); + + PackedCounter _encodedLengths = encodeLengths(a32, s); + bytes memory _dynamicData = encodeDynamic(a32, s); + + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreSwitch.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); + } + + /** Set the full data using individual values */ + function _set(bytes32 key, uint32 u32, uint128 u128, uint32[] memory a32, string memory s) internal { + bytes memory _staticData = encodeStatic(u32, u128); + + PackedCounter _encodedLengths = encodeLengths(a32, s); + bytes memory _dynamicData = encodeDynamic(a32, s); bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.setRecord(_tableId, _keyTuple, _data, getFieldLayout()); + StoreCore.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); } /** Set the full data using individual values (using the specified store) */ function set(IStore _store, bytes32 key, uint32 u32, uint128 u128, uint32[] memory a32, string memory s) internal { - bytes memory _data = encode(u32, u128, a32, s); + bytes memory _staticData = encodeStatic(u32, u128); + + PackedCounter _encodedLengths = encodeLengths(a32, s); + bytes memory _dynamicData = encodeDynamic(a32, s); bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.setRecord(_tableId, _keyTuple, _data, getFieldLayout()); + _store.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); } /** Set the full data using the data struct */ @@ -498,6 +700,11 @@ library Mixed { set(key, _table.u32, _table.u128, _table.a32, _table.s); } + /** Set the full data using the data struct */ + function _set(bytes32 key, MixedData memory _table) internal { + set(key, _table.u32, _table.u128, _table.a32, _table.s); + } + /** Set the full data using the data struct (using the specified store) */ function set(IStore _store, bytes32 key, MixedData memory _table) internal { set(_store, key, _table.u32, _table.u128, _table.a32, _table.s); @@ -533,15 +740,32 @@ library Mixed { } } - /** Tightly pack full data using this table's field layout */ - function encode(uint32 u32, uint128 u128, uint32[] memory a32, string memory s) internal pure returns (bytes memory) { - PackedCounter _encodedLengths; + /** Tightly pack static data using this table's schema */ + function encodeStatic(uint32 u32, uint128 u128) internal pure returns (bytes memory) { + return abi.encodePacked(u32, u128); + } + + /** Tightly pack dynamic data using this table's schema */ + function encodeLengths(uint32[] memory a32, string memory s) internal pure returns (PackedCounter _encodedLengths) { // Lengths are effectively checked during copy by 2**40 bytes exceeding gas limits unchecked { _encodedLengths = PackedCounterLib.pack(a32.length * 4, bytes(s).length); } + } + + /** Tightly pack dynamic data using this table's schema */ + function encodeDynamic(uint32[] memory a32, string memory s) internal pure returns (bytes memory) { + return abi.encodePacked(EncodeArray.encode((a32)), bytes((s))); + } + + /** Tightly pack full data using this table's field layout */ + function encode(uint32 u32, uint128 u128, uint32[] memory a32, string memory s) internal pure returns (bytes memory) { + bytes memory _staticData = encodeStatic(u32, u128); + + PackedCounter _encodedLengths = encodeLengths(a32, s); + bytes memory _dynamicData = encodeDynamic(a32, s); - return abi.encodePacked(u32, u128, _encodedLengths.unwrap(), EncodeArray.encode((a32)), bytes((s))); + return abi.encodePacked(_staticData, _encodedLengths, _dynamicData); } /** Encode keys as a bytes32 array using this table's field layout */ @@ -557,7 +781,15 @@ library Mixed { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + StoreSwitch.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /* Delete all data for given keys */ + function _deleteRecord(bytes32 key) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); } /* Delete all data for given keys (using the specified store) */ @@ -565,6 +797,6 @@ library Mixed { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + _store.deleteRecord(_tableId, _keyTuple, _fieldLayout); } } diff --git a/packages/store/src/codegen/tables/StoreHooks.sol b/packages/store/src/codegen/tables/StoreHooks.sol index 9b84a3549e..43ca3b5fc1 100644 --- a/packages/store/src/codegen/tables/StoreHooks.sol +++ b/packages/store/src/codegen/tables/StoreHooks.sol @@ -21,12 +21,14 @@ import { PackedCounter, PackedCounterLib } from "../../PackedCounter.sol"; bytes32 constant _tableId = bytes32(abi.encodePacked(bytes16("mudstore"), bytes16("StoreHooks"))); bytes32 constant StoreHooksTableId = _tableId; +FieldLayout constant _fieldLayout = FieldLayout.wrap( + 0x0000000100000000000000000000000000000000000000000000000000000000 +); + library StoreHooks { /** Get the table values' field layout */ function getFieldLayout() internal pure returns (FieldLayout) { - uint256[] memory _fieldLayout = new uint256[](0); - - return FieldLayoutLib.encode(_fieldLayout, 1); + return _fieldLayout; } /** Get the table's key schema */ @@ -59,19 +61,17 @@ library StoreHooks { /** Register the table with its config */ function register() internal { - StoreSwitch.registerTable( - _tableId, - getFieldLayout(), - getKeySchema(), - getValueSchema(), - getKeyNames(), - getFieldNames() - ); + StoreSwitch.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + } + + /** Register the table with its config */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Register the table with its config (using the specified store) */ function register(IStore _store) internal { - _store.registerTable(_tableId, getFieldLayout(), getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + _store.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Get value */ @@ -79,7 +79,16 @@ library StoreHooks { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 0, getFieldLayout()); + bytes memory _blob = StoreSwitch.getDynamicField(_tableId, _keyTuple, 0); + return (SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_bytes21()); + } + + /** Get value */ + function _get(bytes32 key) internal view returns (bytes21[] memory value) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + bytes memory _blob = StoreCore.getDynamicField(_tableId, _keyTuple, 0); return (SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_bytes21()); } @@ -88,7 +97,7 @@ library StoreHooks { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = _store.getField(_tableId, _keyTuple, 0, getFieldLayout()); + bytes memory _blob = _store.getDynamicField(_tableId, _keyTuple, 0); return (SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_bytes21()); } @@ -97,7 +106,15 @@ library StoreHooks { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.setField(_tableId, _keyTuple, 0, EncodeArray.encode((value)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 0, EncodeArray.encode((value)), _fieldLayout); + } + + /** Set value */ + function _set(bytes32 key, bytes21[] memory value) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.setField(_tableId, _keyTuple, 0, EncodeArray.encode((value)), _fieldLayout); } /** Set value (using the specified store) */ @@ -105,7 +122,7 @@ library StoreHooks { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.setField(_tableId, _keyTuple, 0, EncodeArray.encode((value)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 0, EncodeArray.encode((value)), _fieldLayout); } /** Get the length of value */ @@ -113,7 +130,18 @@ library StoreHooks { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 0, getFieldLayout()); + uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 0, _fieldLayout); + unchecked { + return _byteLength / 21; + } + } + + /** Get the length of value */ + function _length(bytes32 key) internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + uint256 _byteLength = StoreCore.getFieldLength(_tableId, _keyTuple, 0, _fieldLayout); unchecked { return _byteLength / 21; } @@ -124,7 +152,7 @@ library StoreHooks { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 0, getFieldLayout()); + uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 0, _fieldLayout); unchecked { return _byteLength / 21; } @@ -143,32 +171,46 @@ library StoreHooks { _tableId, _keyTuple, 0, - getFieldLayout(), + _fieldLayout, _index * 21, (_index + 1) * 21 ); - return (Bytes.slice21(_blob, 0)); + return (bytes21(_blob)); } } /** - * Get an item of value (using the specified store) + * Get an item of value * (unchecked, returns invalid data if index overflows) */ - function getItem(IStore _store, bytes32 key, uint256 _index) internal view returns (bytes21) { + function _getItem(bytes32 key, uint256 _index) internal view returns (bytes21) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; unchecked { - bytes memory _blob = _store.getFieldSlice( + bytes memory _blob = StoreCore.getFieldSlice( _tableId, _keyTuple, 0, - getFieldLayout(), + _fieldLayout, _index * 21, (_index + 1) * 21 ); - return (Bytes.slice21(_blob, 0)); + return (bytes21(_blob)); + } + } + + /** + * Get an item of value (using the specified store) + * (unchecked, returns invalid data if index overflows) + */ + function getItem(IStore _store, bytes32 key, uint256 _index) internal view returns (bytes21) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + unchecked { + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 0, _fieldLayout, _index * 21, (_index + 1) * 21); + return (bytes21(_blob)); } } @@ -177,7 +219,15 @@ library StoreHooks { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), getFieldLayout()); + StoreSwitch.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), _fieldLayout); + } + + /** Push an element to value */ + function _push(bytes32 key, bytes21 _element) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), _fieldLayout); } /** Push an element to value (using the specified store) */ @@ -185,7 +235,7 @@ library StoreHooks { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), getFieldLayout()); + _store.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), _fieldLayout); } /** Pop an element from value */ @@ -193,7 +243,15 @@ library StoreHooks { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.popFromField(_tableId, _keyTuple, 0, 21, getFieldLayout()); + StoreSwitch.popFromField(_tableId, _keyTuple, 0, 21, _fieldLayout); + } + + /** Pop an element from value */ + function _pop(bytes32 key) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.popFromField(_tableId, _keyTuple, 0, 21, _fieldLayout); } /** Pop an element from value (using the specified store) */ @@ -201,7 +259,7 @@ library StoreHooks { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.popFromField(_tableId, _keyTuple, 0, 21, getFieldLayout()); + _store.popFromField(_tableId, _keyTuple, 0, 21, _fieldLayout); } /** @@ -213,7 +271,20 @@ library StoreHooks { _keyTuple[0] = key; unchecked { - StoreSwitch.updateInField(_tableId, _keyTuple, 0, _index * 21, abi.encodePacked((_element)), getFieldLayout()); + StoreSwitch.updateInField(_tableId, _keyTuple, 0, _index * 21, abi.encodePacked((_element)), _fieldLayout); + } + } + + /** + * Update an element of value at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ + function _update(bytes32 key, uint256 _index, bytes21 _element) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + unchecked { + StoreCore.updateInField(_tableId, _keyTuple, 0, _index * 21, abi.encodePacked((_element)), _fieldLayout); } } @@ -226,19 +297,30 @@ library StoreHooks { _keyTuple[0] = key; unchecked { - _store.updateInField(_tableId, _keyTuple, 0, _index * 21, abi.encodePacked((_element)), getFieldLayout()); + _store.updateInField(_tableId, _keyTuple, 0, _index * 21, abi.encodePacked((_element)), _fieldLayout); } } - /** Tightly pack full data using this table's field layout */ - function encode(bytes21[] memory value) internal pure returns (bytes memory) { - PackedCounter _encodedLengths; + /** Tightly pack dynamic data using this table's schema */ + function encodeLengths(bytes21[] memory value) internal pure returns (PackedCounter _encodedLengths) { // Lengths are effectively checked during copy by 2**40 bytes exceeding gas limits unchecked { _encodedLengths = PackedCounterLib.pack(value.length * 21); } + } - return abi.encodePacked(_encodedLengths.unwrap(), EncodeArray.encode((value))); + /** Tightly pack dynamic data using this table's schema */ + function encodeDynamic(bytes21[] memory value) internal pure returns (bytes memory) { + return abi.encodePacked(EncodeArray.encode((value))); + } + + /** Tightly pack full data using this table's field layout */ + function encode(bytes21[] memory value) internal pure returns (bytes memory) { + bytes memory _staticData; + PackedCounter _encodedLengths = encodeLengths(value); + bytes memory _dynamicData = encodeDynamic(value); + + return abi.encodePacked(_staticData, _encodedLengths, _dynamicData); } /** Encode keys as a bytes32 array using this table's field layout */ @@ -254,7 +336,15 @@ library StoreHooks { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + StoreSwitch.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /* Delete all data for given keys */ + function _deleteRecord(bytes32 key) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); } /* Delete all data for given keys (using the specified store) */ @@ -262,6 +352,6 @@ library StoreHooks { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + _store.deleteRecord(_tableId, _keyTuple, _fieldLayout); } } diff --git a/packages/store/src/codegen/tables/Tables.sol b/packages/store/src/codegen/tables/Tables.sol index fc82b0dedb..268d19548c 100644 --- a/packages/store/src/codegen/tables/Tables.sol +++ b/packages/store/src/codegen/tables/Tables.sol @@ -21,6 +21,10 @@ import { PackedCounter, PackedCounterLib } from "../../PackedCounter.sol"; bytes32 constant _tableId = bytes32(abi.encodePacked(bytes16("mudstore"), bytes16("Tables"))); bytes32 constant TablesTableId = _tableId; +FieldLayout constant _fieldLayout = FieldLayout.wrap( + 0x0060030220202000000000000000000000000000000000000000000000000000 +); + struct TablesData { bytes32 fieldLayout; bytes32 keySchema; @@ -32,12 +36,7 @@ struct TablesData { library Tables { /** Get the table values' field layout */ function getFieldLayout() internal pure returns (FieldLayout) { - uint256[] memory _fieldLayout = new uint256[](3); - _fieldLayout[0] = 32; - _fieldLayout[1] = 32; - _fieldLayout[2] = 32; - - return FieldLayoutLib.encode(_fieldLayout, 2); + return _fieldLayout; } /** Get the table's key schema */ @@ -78,19 +77,17 @@ library Tables { /** Register the table with its config */ function register() internal { - StoreSwitch.registerTable( - _tableId, - getFieldLayout(), - getKeySchema(), - getValueSchema(), - getKeyNames(), - getFieldNames() - ); + StoreSwitch.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + } + + /** Register the table with its config */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Register the table with its config (using the specified store) */ function register(IStore _store) internal { - _store.registerTable(_tableId, getFieldLayout(), getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + _store.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Get fieldLayout */ @@ -98,8 +95,17 @@ library Tables { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = tableId; - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (Bytes.slice32(_blob, 0)); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (bytes32(_blob)); + } + + /** Get fieldLayout */ + function _getFieldLayout(bytes32 tableId) internal view returns (bytes32 fieldLayout) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = tableId; + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (bytes32(_blob)); } /** Get fieldLayout (using the specified store) */ @@ -107,8 +113,8 @@ library Tables { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = tableId; - bytes memory _blob = _store.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (Bytes.slice32(_blob, 0)); + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (bytes32(_blob)); } /** Set fieldLayout */ @@ -116,7 +122,15 @@ library Tables { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = tableId; - StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((fieldLayout)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((fieldLayout)), _fieldLayout); + } + + /** Set fieldLayout */ + function _setFieldLayout(bytes32 tableId, bytes32 fieldLayout) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = tableId; + + StoreCore.setField(_tableId, _keyTuple, 0, abi.encodePacked((fieldLayout)), _fieldLayout); } /** Set fieldLayout (using the specified store) */ @@ -124,7 +138,7 @@ library Tables { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = tableId; - _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((fieldLayout)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((fieldLayout)), _fieldLayout); } /** Get keySchema */ @@ -132,8 +146,17 @@ library Tables { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = tableId; - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 1, getFieldLayout()); - return (Bytes.slice32(_blob, 0)); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 1, _fieldLayout); + return (bytes32(_blob)); + } + + /** Get keySchema */ + function _getKeySchema(bytes32 tableId) internal view returns (bytes32 keySchema) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = tableId; + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 1, _fieldLayout); + return (bytes32(_blob)); } /** Get keySchema (using the specified store) */ @@ -141,8 +164,8 @@ library Tables { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = tableId; - bytes memory _blob = _store.getField(_tableId, _keyTuple, 1, getFieldLayout()); - return (Bytes.slice32(_blob, 0)); + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 1, _fieldLayout); + return (bytes32(_blob)); } /** Set keySchema */ @@ -150,7 +173,15 @@ library Tables { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = tableId; - StoreSwitch.setField(_tableId, _keyTuple, 1, abi.encodePacked((keySchema)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 1, abi.encodePacked((keySchema)), _fieldLayout); + } + + /** Set keySchema */ + function _setKeySchema(bytes32 tableId, bytes32 keySchema) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = tableId; + + StoreCore.setField(_tableId, _keyTuple, 1, abi.encodePacked((keySchema)), _fieldLayout); } /** Set keySchema (using the specified store) */ @@ -158,7 +189,7 @@ library Tables { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = tableId; - _store.setField(_tableId, _keyTuple, 1, abi.encodePacked((keySchema)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 1, abi.encodePacked((keySchema)), _fieldLayout); } /** Get valueSchema */ @@ -166,8 +197,17 @@ library Tables { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = tableId; - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 2, getFieldLayout()); - return (Bytes.slice32(_blob, 0)); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 2, _fieldLayout); + return (bytes32(_blob)); + } + + /** Get valueSchema */ + function _getValueSchema(bytes32 tableId) internal view returns (bytes32 valueSchema) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = tableId; + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 2, _fieldLayout); + return (bytes32(_blob)); } /** Get valueSchema (using the specified store) */ @@ -175,8 +215,8 @@ library Tables { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = tableId; - bytes memory _blob = _store.getField(_tableId, _keyTuple, 2, getFieldLayout()); - return (Bytes.slice32(_blob, 0)); + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 2, _fieldLayout); + return (bytes32(_blob)); } /** Set valueSchema */ @@ -184,7 +224,15 @@ library Tables { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = tableId; - StoreSwitch.setField(_tableId, _keyTuple, 2, abi.encodePacked((valueSchema)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 2, abi.encodePacked((valueSchema)), _fieldLayout); + } + + /** Set valueSchema */ + function _setValueSchema(bytes32 tableId, bytes32 valueSchema) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = tableId; + + StoreCore.setField(_tableId, _keyTuple, 2, abi.encodePacked((valueSchema)), _fieldLayout); } /** Set valueSchema (using the specified store) */ @@ -192,7 +240,7 @@ library Tables { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = tableId; - _store.setField(_tableId, _keyTuple, 2, abi.encodePacked((valueSchema)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 2, abi.encodePacked((valueSchema)), _fieldLayout); } /** Get abiEncodedKeyNames */ @@ -200,7 +248,16 @@ library Tables { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = tableId; - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 3, getFieldLayout()); + bytes memory _blob = StoreSwitch.getDynamicField(_tableId, _keyTuple, 0); + return (bytes(_blob)); + } + + /** Get abiEncodedKeyNames */ + function _getAbiEncodedKeyNames(bytes32 tableId) internal view returns (bytes memory abiEncodedKeyNames) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = tableId; + + bytes memory _blob = StoreCore.getDynamicField(_tableId, _keyTuple, 0); return (bytes(_blob)); } @@ -212,7 +269,7 @@ library Tables { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = tableId; - bytes memory _blob = _store.getField(_tableId, _keyTuple, 3, getFieldLayout()); + bytes memory _blob = _store.getDynamicField(_tableId, _keyTuple, 0); return (bytes(_blob)); } @@ -221,7 +278,15 @@ library Tables { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = tableId; - StoreSwitch.setField(_tableId, _keyTuple, 3, bytes((abiEncodedKeyNames)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 3, bytes((abiEncodedKeyNames)), _fieldLayout); + } + + /** Set abiEncodedKeyNames */ + function _setAbiEncodedKeyNames(bytes32 tableId, bytes memory abiEncodedKeyNames) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = tableId; + + StoreCore.setField(_tableId, _keyTuple, 3, bytes((abiEncodedKeyNames)), _fieldLayout); } /** Set abiEncodedKeyNames (using the specified store) */ @@ -229,7 +294,7 @@ library Tables { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = tableId; - _store.setField(_tableId, _keyTuple, 3, bytes((abiEncodedKeyNames)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 3, bytes((abiEncodedKeyNames)), _fieldLayout); } /** Get the length of abiEncodedKeyNames */ @@ -237,7 +302,18 @@ library Tables { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = tableId; - uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 3, getFieldLayout()); + uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 3, _fieldLayout); + unchecked { + return _byteLength / 1; + } + } + + /** Get the length of abiEncodedKeyNames */ + function _lengthAbiEncodedKeyNames(bytes32 tableId) internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = tableId; + + uint256 _byteLength = StoreCore.getFieldLength(_tableId, _keyTuple, 3, _fieldLayout); unchecked { return _byteLength / 1; } @@ -248,7 +324,7 @@ library Tables { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = tableId; - uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 3, getFieldLayout()); + uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 3, _fieldLayout); unchecked { return _byteLength / 1; } @@ -267,7 +343,7 @@ library Tables { _tableId, _keyTuple, 3, - getFieldLayout(), + _fieldLayout, _index * 1, (_index + 1) * 1 ); @@ -275,6 +351,20 @@ library Tables { } } + /** + * Get an item of abiEncodedKeyNames + * (unchecked, returns invalid data if index overflows) + */ + function _getItemAbiEncodedKeyNames(bytes32 tableId, uint256 _index) internal view returns (bytes memory) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = tableId; + + unchecked { + bytes memory _blob = StoreCore.getFieldSlice(_tableId, _keyTuple, 3, _fieldLayout, _index * 1, (_index + 1) * 1); + return (bytes(_blob)); + } + } + /** * Get an item of abiEncodedKeyNames (using the specified store) * (unchecked, returns invalid data if index overflows) @@ -288,7 +378,7 @@ library Tables { _keyTuple[0] = tableId; unchecked { - bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 3, getFieldLayout(), _index * 1, (_index + 1) * 1); + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 3, _fieldLayout, _index * 1, (_index + 1) * 1); return (bytes(_blob)); } } @@ -298,7 +388,15 @@ library Tables { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = tableId; - StoreSwitch.pushToField(_tableId, _keyTuple, 3, bytes((_slice)), getFieldLayout()); + StoreSwitch.pushToField(_tableId, _keyTuple, 3, bytes((_slice)), _fieldLayout); + } + + /** Push a slice to abiEncodedKeyNames */ + function _pushAbiEncodedKeyNames(bytes32 tableId, bytes memory _slice) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = tableId; + + StoreCore.pushToField(_tableId, _keyTuple, 3, bytes((_slice)), _fieldLayout); } /** Push a slice to abiEncodedKeyNames (using the specified store) */ @@ -306,7 +404,7 @@ library Tables { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = tableId; - _store.pushToField(_tableId, _keyTuple, 3, bytes((_slice)), getFieldLayout()); + _store.pushToField(_tableId, _keyTuple, 3, bytes((_slice)), _fieldLayout); } /** Pop a slice from abiEncodedKeyNames */ @@ -314,7 +412,15 @@ library Tables { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = tableId; - StoreSwitch.popFromField(_tableId, _keyTuple, 3, 1, getFieldLayout()); + StoreSwitch.popFromField(_tableId, _keyTuple, 3, 1, _fieldLayout); + } + + /** Pop a slice from abiEncodedKeyNames */ + function _popAbiEncodedKeyNames(bytes32 tableId) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = tableId; + + StoreCore.popFromField(_tableId, _keyTuple, 3, 1, _fieldLayout); } /** Pop a slice from abiEncodedKeyNames (using the specified store) */ @@ -322,7 +428,7 @@ library Tables { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = tableId; - _store.popFromField(_tableId, _keyTuple, 3, 1, getFieldLayout()); + _store.popFromField(_tableId, _keyTuple, 3, 1, _fieldLayout); } /** @@ -334,7 +440,20 @@ library Tables { _keyTuple[0] = tableId; unchecked { - StoreSwitch.updateInField(_tableId, _keyTuple, 3, _index * 1, bytes((_slice)), getFieldLayout()); + StoreSwitch.updateInField(_tableId, _keyTuple, 3, _index * 1, bytes((_slice)), _fieldLayout); + } + } + + /** + * Update a slice of abiEncodedKeyNames at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ + function _updateAbiEncodedKeyNames(bytes32 tableId, uint256 _index, bytes memory _slice) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = tableId; + + unchecked { + StoreCore.updateInField(_tableId, _keyTuple, 3, _index * 1, bytes((_slice)), _fieldLayout); } } @@ -347,7 +466,7 @@ library Tables { _keyTuple[0] = tableId; unchecked { - _store.updateInField(_tableId, _keyTuple, 3, _index * 1, bytes((_slice)), getFieldLayout()); + _store.updateInField(_tableId, _keyTuple, 3, _index * 1, bytes((_slice)), _fieldLayout); } } @@ -356,7 +475,16 @@ library Tables { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = tableId; - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 4, getFieldLayout()); + bytes memory _blob = StoreSwitch.getDynamicField(_tableId, _keyTuple, 1); + return (bytes(_blob)); + } + + /** Get abiEncodedFieldNames */ + function _getAbiEncodedFieldNames(bytes32 tableId) internal view returns (bytes memory abiEncodedFieldNames) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = tableId; + + bytes memory _blob = StoreCore.getDynamicField(_tableId, _keyTuple, 1); return (bytes(_blob)); } @@ -368,7 +496,7 @@ library Tables { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = tableId; - bytes memory _blob = _store.getField(_tableId, _keyTuple, 4, getFieldLayout()); + bytes memory _blob = _store.getDynamicField(_tableId, _keyTuple, 1); return (bytes(_blob)); } @@ -377,7 +505,15 @@ library Tables { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = tableId; - StoreSwitch.setField(_tableId, _keyTuple, 4, bytes((abiEncodedFieldNames)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 4, bytes((abiEncodedFieldNames)), _fieldLayout); + } + + /** Set abiEncodedFieldNames */ + function _setAbiEncodedFieldNames(bytes32 tableId, bytes memory abiEncodedFieldNames) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = tableId; + + StoreCore.setField(_tableId, _keyTuple, 4, bytes((abiEncodedFieldNames)), _fieldLayout); } /** Set abiEncodedFieldNames (using the specified store) */ @@ -385,7 +521,7 @@ library Tables { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = tableId; - _store.setField(_tableId, _keyTuple, 4, bytes((abiEncodedFieldNames)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 4, bytes((abiEncodedFieldNames)), _fieldLayout); } /** Get the length of abiEncodedFieldNames */ @@ -393,7 +529,18 @@ library Tables { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = tableId; - uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 4, getFieldLayout()); + uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 4, _fieldLayout); + unchecked { + return _byteLength / 1; + } + } + + /** Get the length of abiEncodedFieldNames */ + function _lengthAbiEncodedFieldNames(bytes32 tableId) internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = tableId; + + uint256 _byteLength = StoreCore.getFieldLength(_tableId, _keyTuple, 4, _fieldLayout); unchecked { return _byteLength / 1; } @@ -404,7 +551,7 @@ library Tables { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = tableId; - uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 4, getFieldLayout()); + uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 4, _fieldLayout); unchecked { return _byteLength / 1; } @@ -423,7 +570,7 @@ library Tables { _tableId, _keyTuple, 4, - getFieldLayout(), + _fieldLayout, _index * 1, (_index + 1) * 1 ); @@ -431,6 +578,20 @@ library Tables { } } + /** + * Get an item of abiEncodedFieldNames + * (unchecked, returns invalid data if index overflows) + */ + function _getItemAbiEncodedFieldNames(bytes32 tableId, uint256 _index) internal view returns (bytes memory) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = tableId; + + unchecked { + bytes memory _blob = StoreCore.getFieldSlice(_tableId, _keyTuple, 4, _fieldLayout, _index * 1, (_index + 1) * 1); + return (bytes(_blob)); + } + } + /** * Get an item of abiEncodedFieldNames (using the specified store) * (unchecked, returns invalid data if index overflows) @@ -444,7 +605,7 @@ library Tables { _keyTuple[0] = tableId; unchecked { - bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 4, getFieldLayout(), _index * 1, (_index + 1) * 1); + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 4, _fieldLayout, _index * 1, (_index + 1) * 1); return (bytes(_blob)); } } @@ -454,7 +615,15 @@ library Tables { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = tableId; - StoreSwitch.pushToField(_tableId, _keyTuple, 4, bytes((_slice)), getFieldLayout()); + StoreSwitch.pushToField(_tableId, _keyTuple, 4, bytes((_slice)), _fieldLayout); + } + + /** Push a slice to abiEncodedFieldNames */ + function _pushAbiEncodedFieldNames(bytes32 tableId, bytes memory _slice) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = tableId; + + StoreCore.pushToField(_tableId, _keyTuple, 4, bytes((_slice)), _fieldLayout); } /** Push a slice to abiEncodedFieldNames (using the specified store) */ @@ -462,7 +631,7 @@ library Tables { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = tableId; - _store.pushToField(_tableId, _keyTuple, 4, bytes((_slice)), getFieldLayout()); + _store.pushToField(_tableId, _keyTuple, 4, bytes((_slice)), _fieldLayout); } /** Pop a slice from abiEncodedFieldNames */ @@ -470,7 +639,15 @@ library Tables { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = tableId; - StoreSwitch.popFromField(_tableId, _keyTuple, 4, 1, getFieldLayout()); + StoreSwitch.popFromField(_tableId, _keyTuple, 4, 1, _fieldLayout); + } + + /** Pop a slice from abiEncodedFieldNames */ + function _popAbiEncodedFieldNames(bytes32 tableId) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = tableId; + + StoreCore.popFromField(_tableId, _keyTuple, 4, 1, _fieldLayout); } /** Pop a slice from abiEncodedFieldNames (using the specified store) */ @@ -478,7 +655,7 @@ library Tables { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = tableId; - _store.popFromField(_tableId, _keyTuple, 4, 1, getFieldLayout()); + _store.popFromField(_tableId, _keyTuple, 4, 1, _fieldLayout); } /** @@ -490,7 +667,20 @@ library Tables { _keyTuple[0] = tableId; unchecked { - StoreSwitch.updateInField(_tableId, _keyTuple, 4, _index * 1, bytes((_slice)), getFieldLayout()); + StoreSwitch.updateInField(_tableId, _keyTuple, 4, _index * 1, bytes((_slice)), _fieldLayout); + } + } + + /** + * Update a slice of abiEncodedFieldNames at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ + function _updateAbiEncodedFieldNames(bytes32 tableId, uint256 _index, bytes memory _slice) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = tableId; + + unchecked { + StoreCore.updateInField(_tableId, _keyTuple, 4, _index * 1, bytes((_slice)), _fieldLayout); } } @@ -503,7 +693,7 @@ library Tables { _keyTuple[0] = tableId; unchecked { - _store.updateInField(_tableId, _keyTuple, 4, _index * 1, bytes((_slice)), getFieldLayout()); + _store.updateInField(_tableId, _keyTuple, 4, _index * 1, bytes((_slice)), _fieldLayout); } } @@ -512,7 +702,16 @@ library Tables { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = tableId; - bytes memory _blob = StoreSwitch.getRecord(_tableId, _keyTuple, getFieldLayout()); + bytes memory _blob = StoreSwitch.getRecord(_tableId, _keyTuple, _fieldLayout); + return decode(_blob); + } + + /** Get the full data */ + function _get(bytes32 tableId) internal view returns (TablesData memory _table) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = tableId; + + bytes memory _blob = StoreCore.getRecord(_tableId, _keyTuple, _fieldLayout); return decode(_blob); } @@ -521,7 +720,7 @@ library Tables { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = tableId; - bytes memory _blob = _store.getRecord(_tableId, _keyTuple, getFieldLayout()); + bytes memory _blob = _store.getRecord(_tableId, _keyTuple, _fieldLayout); return decode(_blob); } @@ -534,12 +733,35 @@ library Tables { bytes memory abiEncodedKeyNames, bytes memory abiEncodedFieldNames ) internal { - bytes memory _data = encode(fieldLayout, keySchema, valueSchema, abiEncodedKeyNames, abiEncodedFieldNames); + bytes memory _staticData = encodeStatic(fieldLayout, keySchema, valueSchema); + + PackedCounter _encodedLengths = encodeLengths(abiEncodedKeyNames, abiEncodedFieldNames); + bytes memory _dynamicData = encodeDynamic(abiEncodedKeyNames, abiEncodedFieldNames); + + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = tableId; + + StoreSwitch.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); + } + + /** Set the full data using individual values */ + function _set( + bytes32 tableId, + bytes32 fieldLayout, + bytes32 keySchema, + bytes32 valueSchema, + bytes memory abiEncodedKeyNames, + bytes memory abiEncodedFieldNames + ) internal { + bytes memory _staticData = encodeStatic(fieldLayout, keySchema, valueSchema); + + PackedCounter _encodedLengths = encodeLengths(abiEncodedKeyNames, abiEncodedFieldNames); + bytes memory _dynamicData = encodeDynamic(abiEncodedKeyNames, abiEncodedFieldNames); bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = tableId; - StoreSwitch.setRecord(_tableId, _keyTuple, _data, getFieldLayout()); + StoreCore.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); } /** Set the full data using individual values (using the specified store) */ @@ -552,12 +774,15 @@ library Tables { bytes memory abiEncodedKeyNames, bytes memory abiEncodedFieldNames ) internal { - bytes memory _data = encode(fieldLayout, keySchema, valueSchema, abiEncodedKeyNames, abiEncodedFieldNames); + bytes memory _staticData = encodeStatic(fieldLayout, keySchema, valueSchema); + + PackedCounter _encodedLengths = encodeLengths(abiEncodedKeyNames, abiEncodedFieldNames); + bytes memory _dynamicData = encodeDynamic(abiEncodedKeyNames, abiEncodedFieldNames); bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = tableId; - _store.setRecord(_tableId, _keyTuple, _data, getFieldLayout()); + _store.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); } /** Set the full data using the data struct */ @@ -572,6 +797,18 @@ library Tables { ); } + /** Set the full data using the data struct */ + function _set(bytes32 tableId, TablesData memory _table) internal { + set( + tableId, + _table.fieldLayout, + _table.keySchema, + _table.valueSchema, + _table.abiEncodedKeyNames, + _table.abiEncodedFieldNames + ); + } + /** Set the full data using the data struct (using the specified store) */ function set(IStore _store, bytes32 tableId, TablesData memory _table) internal { set( @@ -617,29 +854,48 @@ library Tables { } } - /** Tightly pack full data using this table's field layout */ - function encode( + /** Tightly pack static data using this table's schema */ + function encodeStatic( bytes32 fieldLayout, bytes32 keySchema, - bytes32 valueSchema, + bytes32 valueSchema + ) internal pure returns (bytes memory) { + return abi.encodePacked(fieldLayout, keySchema, valueSchema); + } + + /** Tightly pack dynamic data using this table's schema */ + function encodeLengths( bytes memory abiEncodedKeyNames, bytes memory abiEncodedFieldNames - ) internal pure returns (bytes memory) { - PackedCounter _encodedLengths; + ) internal pure returns (PackedCounter _encodedLengths) { // Lengths are effectively checked during copy by 2**40 bytes exceeding gas limits unchecked { _encodedLengths = PackedCounterLib.pack(bytes(abiEncodedKeyNames).length, bytes(abiEncodedFieldNames).length); } + } - return - abi.encodePacked( - fieldLayout, - keySchema, - valueSchema, - _encodedLengths.unwrap(), - bytes((abiEncodedKeyNames)), - bytes((abiEncodedFieldNames)) - ); + /** Tightly pack dynamic data using this table's schema */ + function encodeDynamic( + bytes memory abiEncodedKeyNames, + bytes memory abiEncodedFieldNames + ) internal pure returns (bytes memory) { + return abi.encodePacked(bytes((abiEncodedKeyNames)), bytes((abiEncodedFieldNames))); + } + + /** Tightly pack full data using this table's field layout */ + function encode( + bytes32 fieldLayout, + bytes32 keySchema, + bytes32 valueSchema, + bytes memory abiEncodedKeyNames, + bytes memory abiEncodedFieldNames + ) internal pure returns (bytes memory) { + bytes memory _staticData = encodeStatic(fieldLayout, keySchema, valueSchema); + + PackedCounter _encodedLengths = encodeLengths(abiEncodedKeyNames, abiEncodedFieldNames); + bytes memory _dynamicData = encodeDynamic(abiEncodedKeyNames, abiEncodedFieldNames); + + return abi.encodePacked(_staticData, _encodedLengths, _dynamicData); } /** Encode keys as a bytes32 array using this table's field layout */ @@ -655,7 +911,15 @@ library Tables { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = tableId; - StoreSwitch.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + StoreSwitch.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /* Delete all data for given keys */ + function _deleteRecord(bytes32 tableId) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = tableId; + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); } /* Delete all data for given keys (using the specified store) */ @@ -663,6 +927,6 @@ library Tables { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = tableId; - _store.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + _store.deleteRecord(_tableId, _keyTuple, _fieldLayout); } } diff --git a/packages/store/src/codegen/tables/Vector2.sol b/packages/store/src/codegen/tables/Vector2.sol index 82a58d420b..707c7a3dac 100644 --- a/packages/store/src/codegen/tables/Vector2.sol +++ b/packages/store/src/codegen/tables/Vector2.sol @@ -21,6 +21,10 @@ import { PackedCounter, PackedCounterLib } from "../../PackedCounter.sol"; bytes32 constant _tableId = bytes32(abi.encodePacked(bytes16("mudstore"), bytes16("Vector2"))); bytes32 constant Vector2TableId = _tableId; +FieldLayout constant _fieldLayout = FieldLayout.wrap( + 0x0008020004040000000000000000000000000000000000000000000000000000 +); + struct Vector2Data { uint32 x; uint32 y; @@ -29,11 +33,7 @@ struct Vector2Data { library Vector2 { /** Get the table values' field layout */ function getFieldLayout() internal pure returns (FieldLayout) { - uint256[] memory _fieldLayout = new uint256[](2); - _fieldLayout[0] = 4; - _fieldLayout[1] = 4; - - return FieldLayoutLib.encode(_fieldLayout, 0); + return _fieldLayout; } /** Get the table's key schema */ @@ -68,19 +68,17 @@ library Vector2 { /** Register the table with its config */ function register() internal { - StoreSwitch.registerTable( - _tableId, - getFieldLayout(), - getKeySchema(), - getValueSchema(), - getKeyNames(), - getFieldNames() - ); + StoreSwitch.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + } + + /** Register the table with its config */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Register the table with its config (using the specified store) */ function register(IStore _store) internal { - _store.registerTable(_tableId, getFieldLayout(), getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + _store.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Get x */ @@ -88,8 +86,17 @@ library Vector2 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (uint32(Bytes.slice4(_blob, 0))); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint32(bytes4(_blob))); + } + + /** Get x */ + function _getX(bytes32 key) internal view returns (uint32 x) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint32(bytes4(_blob))); } /** Get x (using the specified store) */ @@ -97,8 +104,8 @@ library Vector2 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = _store.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (uint32(Bytes.slice4(_blob, 0))); + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint32(bytes4(_blob))); } /** Set x */ @@ -106,7 +113,15 @@ library Vector2 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((x)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((x)), _fieldLayout); + } + + /** Set x */ + function _setX(bytes32 key, uint32 x) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.setField(_tableId, _keyTuple, 0, abi.encodePacked((x)), _fieldLayout); } /** Set x (using the specified store) */ @@ -114,7 +129,7 @@ library Vector2 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((x)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((x)), _fieldLayout); } /** Get y */ @@ -122,8 +137,17 @@ library Vector2 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 1, getFieldLayout()); - return (uint32(Bytes.slice4(_blob, 0))); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 1, _fieldLayout); + return (uint32(bytes4(_blob))); + } + + /** Get y */ + function _getY(bytes32 key) internal view returns (uint32 y) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 1, _fieldLayout); + return (uint32(bytes4(_blob))); } /** Get y (using the specified store) */ @@ -131,8 +155,8 @@ library Vector2 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = _store.getField(_tableId, _keyTuple, 1, getFieldLayout()); - return (uint32(Bytes.slice4(_blob, 0))); + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 1, _fieldLayout); + return (uint32(bytes4(_blob))); } /** Set y */ @@ -140,7 +164,15 @@ library Vector2 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.setField(_tableId, _keyTuple, 1, abi.encodePacked((y)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 1, abi.encodePacked((y)), _fieldLayout); + } + + /** Set y */ + function _setY(bytes32 key, uint32 y) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.setField(_tableId, _keyTuple, 1, abi.encodePacked((y)), _fieldLayout); } /** Set y (using the specified store) */ @@ -148,7 +180,7 @@ library Vector2 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.setField(_tableId, _keyTuple, 1, abi.encodePacked((y)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 1, abi.encodePacked((y)), _fieldLayout); } /** Get the full data */ @@ -156,7 +188,16 @@ library Vector2 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = StoreSwitch.getRecord(_tableId, _keyTuple, getFieldLayout()); + bytes memory _blob = StoreSwitch.getRecord(_tableId, _keyTuple, _fieldLayout); + return decode(_blob); + } + + /** Get the full data */ + function _get(bytes32 key) internal view returns (Vector2Data memory _table) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + bytes memory _blob = StoreCore.getRecord(_tableId, _keyTuple, _fieldLayout); return decode(_blob); } @@ -165,28 +206,47 @@ library Vector2 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = _store.getRecord(_tableId, _keyTuple, getFieldLayout()); + bytes memory _blob = _store.getRecord(_tableId, _keyTuple, _fieldLayout); return decode(_blob); } /** Set the full data using individual values */ function set(bytes32 key, uint32 x, uint32 y) internal { - bytes memory _data = encode(x, y); + bytes memory _staticData = encodeStatic(x, y); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; + + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreSwitch.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); + } + + /** Set the full data using individual values */ + function _set(bytes32 key, uint32 x, uint32 y) internal { + bytes memory _staticData = encodeStatic(x, y); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.setRecord(_tableId, _keyTuple, _data, getFieldLayout()); + StoreCore.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); } /** Set the full data using individual values (using the specified store) */ function set(IStore _store, bytes32 key, uint32 x, uint32 y) internal { - bytes memory _data = encode(x, y); + bytes memory _staticData = encodeStatic(x, y); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.setRecord(_tableId, _keyTuple, _data, getFieldLayout()); + _store.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); } /** Set the full data using the data struct */ @@ -194,6 +254,11 @@ library Vector2 { set(key, _table.x, _table.y); } + /** Set the full data using the data struct */ + function _set(bytes32 key, Vector2Data memory _table) internal { + set(key, _table.x, _table.y); + } + /** Set the full data using the data struct (using the specified store) */ function set(IStore _store, bytes32 key, Vector2Data memory _table) internal { set(_store, key, _table.x, _table.y); @@ -206,9 +271,19 @@ library Vector2 { _table.y = (uint32(Bytes.slice4(_blob, 4))); } + /** Tightly pack static data using this table's schema */ + function encodeStatic(uint32 x, uint32 y) internal pure returns (bytes memory) { + return abi.encodePacked(x, y); + } + /** Tightly pack full data using this table's field layout */ function encode(uint32 x, uint32 y) internal pure returns (bytes memory) { - return abi.encodePacked(x, y); + bytes memory _staticData = encodeStatic(x, y); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; + + return abi.encodePacked(_staticData, _encodedLengths, _dynamicData); } /** Encode keys as a bytes32 array using this table's field layout */ @@ -224,7 +299,15 @@ library Vector2 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + StoreSwitch.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /* Delete all data for given keys */ + function _deleteRecord(bytes32 key) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); } /* Delete all data for given keys (using the specified store) */ @@ -232,6 +315,6 @@ library Vector2 { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + _store.deleteRecord(_tableId, _keyTuple, _fieldLayout); } } diff --git a/packages/store/test/EchoSubscriber.sol b/packages/store/test/EchoSubscriber.sol index 505502dcde..e416d1753c 100644 --- a/packages/store/test/EchoSubscriber.sol +++ b/packages/store/test/EchoSubscriber.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; +import { PackedCounter } from "../src/PackedCounter.sol"; import { FieldLayout } from "../src/FieldLayout.sol"; import { StoreHook } from "../src/StoreHook.sol"; @@ -10,39 +11,43 @@ contract EchoSubscriber is StoreHook { function onBeforeSetRecord( bytes32 tableId, bytes32[] memory keyTuple, - bytes memory data, + bytes memory staticData, + PackedCounter encodedLengths, + bytes memory dynamicData, FieldLayout fieldLayout ) public { - emit HookCalled(abi.encode(tableId, keyTuple, data, fieldLayout)); + emit HookCalled(abi.encode(tableId, keyTuple, staticData, encodedLengths, dynamicData, fieldLayout)); } function onAfterSetRecord( bytes32 tableId, bytes32[] memory keyTuple, - bytes memory data, + bytes memory staticData, + PackedCounter encodedLengths, + bytes memory dynamicData, FieldLayout fieldLayout ) public { - emit HookCalled(abi.encode(tableId, keyTuple, data, fieldLayout)); + emit HookCalled(abi.encode(tableId, keyTuple, staticData, encodedLengths, dynamicData, fieldLayout)); } function onBeforeSetField( bytes32 tableId, bytes32[] memory keyTuple, - uint8 schemaIndex, + uint8 fieldIndex, bytes memory data, FieldLayout fieldLayout ) public { - emit HookCalled(abi.encode(tableId, keyTuple, schemaIndex, data, fieldLayout)); + emit HookCalled(abi.encode(tableId, keyTuple, fieldIndex, data, fieldLayout)); } function onAfterSetField( bytes32 tableId, bytes32[] memory keyTuple, - uint8 schemaIndex, + uint8 fieldIndex, bytes memory data, FieldLayout fieldLayout ) public { - emit HookCalled(abi.encode(tableId, keyTuple, schemaIndex, data, fieldLayout)); + emit HookCalled(abi.encode(tableId, keyTuple, fieldIndex, data, fieldLayout)); } function onBeforeDeleteRecord(bytes32 tableId, bytes32[] memory keyTuple, FieldLayout fieldLayout) public { diff --git a/packages/store/test/FieldLayout.t.sol b/packages/store/test/FieldLayout.t.sol index c8555fb18d..c8f302e1fa 100644 --- a/packages/store/test/FieldLayout.t.sol +++ b/packages/store/test/FieldLayout.t.sol @@ -204,4 +204,35 @@ contract FieldLayoutTest is Test, GasReporter { assertFalse(empty); } + + function testHex() public { + uint256[] memory _fieldLayout = new uint256[](24); + _fieldLayout[0] = 1; + _fieldLayout[1] = 2; + _fieldLayout[2] = 3; + _fieldLayout[3] = 4; + _fieldLayout[4] = 5; + _fieldLayout[5] = 6; + _fieldLayout[6] = 7; + _fieldLayout[7] = 8; + _fieldLayout[8] = 9; + _fieldLayout[9] = 10; + _fieldLayout[10] = 11; + _fieldLayout[11] = 12; + _fieldLayout[12] = 13; + _fieldLayout[13] = 14; + _fieldLayout[14] = 15; + _fieldLayout[15] = 16; + _fieldLayout[16] = 17; + _fieldLayout[17] = 18; + _fieldLayout[18] = 19; + _fieldLayout[19] = 20; + _fieldLayout[20] = 21; + _fieldLayout[21] = 22; + _fieldLayout[22] = 23; + _fieldLayout[23] = 32; + + FieldLayout encodedFieldLayout = FieldLayoutLib.encode(_fieldLayout, 4); + assertEq(encodedFieldLayout.unwrap(), hex"013418040102030405060708090a0b0c0d0e0f10111213141516172000000000"); + } } diff --git a/packages/store/test/Gas.t.sol b/packages/store/test/Gas.t.sol index 884b019ebd..ea0a6299e9 100644 --- a/packages/store/test/Gas.t.sol +++ b/packages/store/test/Gas.t.sol @@ -6,6 +6,7 @@ import { GasReporter } from "@latticexyz/gas-report/src/GasReporter.sol"; import { Bytes } from "../src/Bytes.sol"; import { SliceLib } from "../src/Slice.sol"; import { Storage } from "../src/Storage.sol"; +import { PackedCounter } from "../src/PackedCounter.sol"; import { Mixed, MixedData } from "../src/codegen/Tables.sol"; contract SomeContract { @@ -21,6 +22,14 @@ contract GasTest is Test, GasReporter { mixed.a32[1] = 2; mixed.a32[2] = 3; + startGasReport("abi encode (static)"); + bytes memory abiEncodedStatic = abi.encode(mixed.u32, mixed.u128); + endGasReport(); + + startGasReport("abi encode (dynamic)"); + bytes memory abiEncodedDynamic = abi.encode(mixed.a32, mixed.s); + endGasReport(); + startGasReport("abi encode"); bytes memory abiEncoded = abi.encode(mixed); endGasReport(); @@ -29,6 +38,19 @@ contract GasTest is Test, GasReporter { MixedData memory abiDecoded = abi.decode(abiEncoded, (MixedData)); endGasReport(); + startGasReport("custom encode (static)"); + bytes memory customEncodedStatic = Mixed.encodeStatic(mixed.u32, mixed.u128); + endGasReport(); + + startGasReport("custom encode (length)"); + PackedCounter packedCounter = Mixed.encodeLengths(mixed.a32, mixed.s); + endGasReport(); + PackedCounter.unwrap(packedCounter); + + startGasReport("custom encode (dynamic)"); + bytes memory customEncodedDynamic = Mixed.encodeDynamic(mixed.a32, mixed.s); + endGasReport(); + startGasReport("custom encode"); bytes memory customEncoded = Mixed.encode(mixed.u32, mixed.u128, mixed.a32, mixed.s); endGasReport(); @@ -38,6 +60,16 @@ contract GasTest is Test, GasReporter { endGasReport(); console.log("Length comparison: abi encode %s, custom %s", abiEncoded.length, customEncoded.length); + console.log( + "Length comparison (static): abi encode %s, custom %s", + abiEncodedStatic.length, + customEncodedStatic.length + ); + console.log( + "Length comparison (dynamic): abi encode %s, custom %s", + abiEncodedDynamic.length, + customEncodedDynamic.length + ); startGasReport("pass abi encoded bytes to external contract"); someContract.doSomethingWithBytes(abiEncoded); diff --git a/packages/store/test/GasStorageLoad.t.sol b/packages/store/test/GasStorageLoad.t.sol index 3991d25e12..ab13727d31 100644 --- a/packages/store/test/GasStorageLoad.t.sol +++ b/packages/store/test/GasStorageLoad.t.sol @@ -67,6 +67,9 @@ contract GasStorageLoadTest is Test, GasReporter { bytes memory encodedPartial = abi.encodePacked(valuePartial); bytes memory encoded9Words = abi.encodePacked(value9Words.length, value9Words); + bytes32 encodedFieldSimple = valueSimple; + bytes32 encodedFieldPartial = valuePartial; + startGasReport("MUD storage load (cold, 1 word)"); encodedSimple = Storage.load(SolidityStorage.STORAGE_SLOT_SIMPLE, encodedSimple.length, 0); endGasReport(); @@ -85,10 +88,19 @@ contract GasStorageLoadTest is Test, GasReporter { encodedSimple = Storage.load(SolidityStorage.STORAGE_SLOT_SIMPLE, encodedSimple.length, 0); endGasReport(); + startGasReport("MUD storage load field (warm, 1 word)"); + encodedFieldSimple = Storage.loadField(SolidityStorage.STORAGE_SLOT_SIMPLE, encodedSimple.length, 0); + endGasReport(); + startGasReport("MUD storage load (warm, 1 word, partial)"); encodedPartial = Storage.load(SolidityStorage.STORAGE_SLOT_PARTIAL, encodedPartial.length, 16); endGasReport(); + encodedFieldPartial = Storage.loadField(SolidityStorage.STORAGE_SLOT_PARTIAL, encodedSimple.length, 16); + startGasReport("MUD storage load field (warm, 1 word, partial)"); + encodedFieldPartial = Storage.loadField(SolidityStorage.STORAGE_SLOT_PARTIAL, encodedSimple.length, 16); + endGasReport(); + startGasReport("MUD storage load (warm, 10 words)"); encoded9Words = Storage.load(SolidityStorage.STORAGE_SLOT_BYTES, encoded9Words.length, 0); endGasReport(); diff --git a/packages/store/test/MirrorSubscriber.sol b/packages/store/test/MirrorSubscriber.sol index 34d6179682..38681feb23 100644 --- a/packages/store/test/MirrorSubscriber.sol +++ b/packages/store/test/MirrorSubscriber.sol @@ -3,6 +3,7 @@ pragma solidity >=0.8.0; import { IStore } from "../src/IStore.sol"; import { StoreHook } from "../src/StoreHook.sol"; +import { PackedCounter } from "../src/PackedCounter.sol"; import { StoreSwitch } from "../src/StoreSwitch.sol"; import { FieldLayout } from "../src/FieldLayout.sol"; import { Schema } from "../src/Schema.sol"; @@ -27,17 +28,21 @@ contract MirrorSubscriber is StoreHook { function onBeforeSetRecord( bytes32 tableId, bytes32[] memory keyTuple, - bytes memory data, + bytes calldata staticData, + PackedCounter encodedLengths, + bytes calldata dynamicData, FieldLayout fieldLayout ) public { - if (tableId != tableId) revert("invalid tableId"); - StoreSwitch.setRecord(indexerTableId, keyTuple, data, fieldLayout); + if (tableId != _tableId) revert("invalid table"); + StoreSwitch.setRecord(indexerTableId, keyTuple, staticData, encodedLengths, dynamicData, fieldLayout); } function onAfterSetRecord( bytes32 tableId, bytes32[] memory keyTuple, - bytes memory data, + bytes calldata staticData, + PackedCounter encodedLengths, + bytes calldata dynamicData, FieldLayout fieldLayout ) public { // NOOP @@ -46,12 +51,12 @@ contract MirrorSubscriber is StoreHook { function onBeforeSetField( bytes32 tableId, bytes32[] memory keyTuple, - uint8 schemaIndex, + uint8 fieldIndex, bytes memory data, FieldLayout fieldLayout ) public { if (tableId != tableId) revert("invalid tableId"); - StoreSwitch.setField(indexerTableId, keyTuple, schemaIndex, data, fieldLayout); + StoreSwitch.setField(indexerTableId, keyTuple, fieldIndex, data, fieldLayout); } function onAfterSetField(bytes32, bytes32[] memory, uint8, bytes memory, FieldLayout) public { diff --git a/packages/store/test/PackedCounter.t.sol b/packages/store/test/PackedCounter.t.sol index 5da1cfcf2e..18b8bb14e9 100644 --- a/packages/store/test/PackedCounter.t.sol +++ b/packages/store/test/PackedCounter.t.sol @@ -102,4 +102,9 @@ contract PackedCounterTest is Test, GasReporter { endGasReport(); assertEq(packedCounter.total(), 1 * 32 + 2 * 20 + 3 * 1 + 4 * 16); } + + function testHexEncoding() public { + PackedCounter packedCounter = PackedCounterLib.pack(160, 544); + assertEq(packedCounter.unwrap(), hex"000000000000000000000000000000000000022000000000a0000000000002c0"); + } } diff --git a/packages/store/test/RevertSubscriber.sol b/packages/store/test/RevertSubscriber.sol index 2d157d9850..7c2ae092f7 100644 --- a/packages/store/test/RevertSubscriber.sol +++ b/packages/store/test/RevertSubscriber.sol @@ -1,15 +1,30 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; -import { FieldLayout } from "../src/FieldLayout.sol"; import { StoreHook } from "../src/StoreHook.sol"; +import { FieldLayout } from "../src/FieldLayout.sol"; +import { PackedCounter } from "../src/PackedCounter.sol"; contract RevertSubscriber is StoreHook { - function onBeforeSetRecord(bytes32, bytes32[] memory, bytes memory, FieldLayout) public pure { + function onBeforeSetRecord( + bytes32, + bytes32[] memory, + bytes memory, + PackedCounter, + bytes memory, + FieldLayout + ) public pure { revert("onBeforeSetRecord"); } - function onAfterSetRecord(bytes32, bytes32[] memory, bytes memory, FieldLayout) public pure { + function onAfterSetRecord( + bytes32, + bytes32[] memory, + bytes memory, + PackedCounter, + bytes memory, + FieldLayout + ) public pure { revert("onAfterSetRecord"); } diff --git a/packages/store/test/Storage.t.sol b/packages/store/test/Storage.t.sol index 1d6005aa6d..9c6d13ebd9 100644 --- a/packages/store/test/Storage.t.sol +++ b/packages/store/test/Storage.t.sol @@ -75,4 +75,22 @@ contract StorageTest is Test, GasReporter { Storage.store({ storagePointer: uint256(storagePointer), offset: offset, data: data }); assertEq(Storage.load({ storagePointer: uint256(storagePointer), length: data.length, offset: offset }), data); } + + function testStoreLoadFieldBytes32Fuzzy(bytes32 data, uint256 storagePointer, uint256 offset) public { + vm.assume(offset < type(uint256).max); + vm.assume(storagePointer > 0); + vm.assume(storagePointer < type(uint256).max - offset); + + Storage.store({ storagePointer: storagePointer, offset: offset, data: abi.encodePacked((data)) }); + assertEq(Storage.loadField({ storagePointer: storagePointer, length: 32, offset: offset }), data); + } + + function testStoreLoadFieldBytes16Fuzzy(bytes16 data, uint256 storagePointer, uint256 offset) public { + vm.assume(offset < type(uint256).max); + vm.assume(storagePointer > 0); + vm.assume(storagePointer < type(uint256).max - offset); + + Storage.store({ storagePointer: storagePointer, offset: offset, data: abi.encodePacked((data)) }); + assertEq(bytes16(Storage.loadField({ storagePointer: storagePointer, length: 16, offset: offset })), data); + } } diff --git a/packages/store/test/StoreCore.t.sol b/packages/store/test/StoreCore.t.sol index 6f502597f2..fc1f25134e 100644 --- a/packages/store/test/StoreCore.t.sol +++ b/packages/store/test/StoreCore.t.sol @@ -64,13 +64,9 @@ contract StoreCoreTest is Test, StoreMock { emit StoreSetRecord( TablesTableId, keyTuple, - Tables.encode( - fieldLayout.unwrap(), - keySchema.unwrap(), - valueSchema.unwrap(), - abi.encode(keyNames), - abi.encode(fieldNames) - ) + Tables.encodeStatic(fieldLayout.unwrap(), keySchema.unwrap(), valueSchema.unwrap()), + Tables.encodeLengths(abi.encode(keyNames), abi.encode(fieldNames)).unwrap(), + Tables.encodeDynamic(abi.encode(keyNames), abi.encode(fieldNames)) ); IStore(this).registerTable(tableId, fieldLayout, keySchema, valueSchema, keyNames, fieldNames); @@ -227,21 +223,21 @@ contract StoreCoreTest is Test, StoreMock { IStore(this).registerTable(tableId, fieldLayout, defaultKeySchema, valueSchema, new string[](1), new string[](4)); // Set data - bytes memory data = abi.encodePacked(bytes1(0x01), bytes2(0x0203), bytes1(0x04), bytes2(0x0506)); + bytes memory staticData = abi.encodePacked(bytes1(0x01), bytes2(0x0203), bytes1(0x04), bytes2(0x0506)); bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = "some key"; // Expect a StoreSetRecord event to be emitted vm.expectEmit(true, true, true, true); - emit StoreSetRecord(tableId, keyTuple, data); + emit StoreSetRecord(tableId, keyTuple, staticData, bytes32(0), new bytes(0)); - IStore(this).setRecord(tableId, keyTuple, data, fieldLayout); + IStore(this).setRecord(tableId, keyTuple, staticData, PackedCounter.wrap(bytes32(0)), new bytes(0), fieldLayout); // Get data bytes memory loadedData = IStore(this).getRecord(tableId, keyTuple, fieldLayout); - assertTrue(Bytes.equals(data, loadedData)); + assertTrue(Bytes.equals(staticData, loadedData)); } function testFailSetAndGetStaticData() public { @@ -257,24 +253,26 @@ contract StoreCoreTest is Test, StoreMock { IStore(this).registerTable(tableId, fieldLayout, defaultKeySchema, valueSchema, new string[](1), new string[](4)); // Set data - bytes memory data = abi.encodePacked(bytes1(0x01), bytes2(0x0203), bytes1(0x04)); + bytes memory staticData = abi.encodePacked(bytes1(0x01), bytes2(0x0203), bytes1(0x04)); bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = "some key"; // This should fail because the data is not 6 bytes long - IStore(this).setRecord(tableId, keyTuple, data, fieldLayout); + IStore(this).setRecord(tableId, keyTuple, staticData, PackedCounter.wrap(bytes32(0)), new bytes(0), fieldLayout); } function testSetAndGetStaticDataSpanningWords() public { // Register table FieldLayout fieldLayout = FieldLayoutEncodeHelper.encode(16, 32, 0); - Schema valueSchema = SchemaEncodeHelper.encode(SchemaType.UINT128, SchemaType.UINT256); - bytes32 tableId = keccak256("some.tableId"); - IStore(this).registerTable(tableId, fieldLayout, defaultKeySchema, valueSchema, new string[](1), new string[](2)); + bytes32 tableId = keccak256("some.table"); + { + Schema valueSchema = SchemaEncodeHelper.encode(SchemaType.UINT128, SchemaType.UINT256); + IStore(this).registerTable(tableId, fieldLayout, defaultKeySchema, valueSchema, new string[](1), new string[](2)); + } // Set data - bytes memory data = abi.encodePacked( + bytes memory staticData = abi.encodePacked( bytes16(0x0102030405060708090a0b0c0d0e0f10), bytes32(0x1112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f30) ); @@ -284,14 +282,14 @@ contract StoreCoreTest is Test, StoreMock { // Expect a StoreSetRecord event to be emitted vm.expectEmit(true, true, true, true); - emit StoreSetRecord(tableId, keyTuple, data); + emit StoreSetRecord(tableId, keyTuple, staticData, bytes32(0), new bytes(0)); - IStore(this).setRecord(tableId, keyTuple, data, fieldLayout); + IStore(this).setRecord(tableId, keyTuple, staticData, PackedCounter.wrap(bytes32(0)), new bytes(0), fieldLayout); // Get data bytes memory loadedData = IStore(this).getRecord(tableId, keyTuple, fieldLayout); - assertTrue(Bytes.equals(data, loadedData)); + assertTrue(Bytes.equals(staticData, loadedData)); } function testSetAndGetDynamicData() public { @@ -299,12 +297,14 @@ contract StoreCoreTest is Test, StoreMock { // Register table FieldLayout fieldLayout = FieldLayoutEncodeHelper.encode(16, 2); - Schema valueSchema = SchemaEncodeHelper.encode( - SchemaType.UINT128, - SchemaType.UINT32_ARRAY, - SchemaType.UINT32_ARRAY - ); - IStore(this).registerTable(tableId, fieldLayout, defaultKeySchema, valueSchema, new string[](1), new string[](3)); + { + Schema valueSchema = SchemaEncodeHelper.encode( + SchemaType.UINT128, + SchemaType.UINT32_ARRAY, + SchemaType.UINT32_ARRAY + ); + IStore(this).registerTable(tableId, fieldLayout, defaultKeySchema, valueSchema, new string[](1), new string[](3)); + } bytes16 firstDataBytes = bytes16(0x0102030405060708090a0b0c0d0e0f10); @@ -331,6 +331,8 @@ contract StoreCoreTest is Test, StoreMock { } // Concat data + bytes memory staticData = abi.encodePacked(firstDataBytes); + bytes memory dynamicData = abi.encodePacked(secondDataBytes, thirdDataBytes); bytes memory data = abi.encodePacked( firstDataBytes, encodedDynamicLength.unwrap(), @@ -344,10 +346,10 @@ contract StoreCoreTest is Test, StoreMock { // Expect a StoreSetRecord event to be emitted vm.expectEmit(true, true, true, true); - emit StoreSetRecord(tableId, keyTuple, data); + emit StoreSetRecord(tableId, keyTuple, staticData, encodedDynamicLength.unwrap(), dynamicData); // Set data - IStore(this).setRecord(tableId, keyTuple, data, fieldLayout); + IStore(this).setRecord(tableId, keyTuple, staticData, encodedDynamicLength, dynamicData, fieldLayout); // Get data bytes memory loadedData = IStore(this).getRecord(tableId, keyTuple, fieldLayout); @@ -374,13 +376,15 @@ contract StoreCoreTest is Test, StoreMock { // Register table FieldLayout fieldLayout = FieldLayoutEncodeHelper.encode(16, 32, 2); - Schema valueSchema = SchemaEncodeHelper.encode( - SchemaType.UINT128, - SchemaType.UINT256, - SchemaType.UINT32_ARRAY, - SchemaType.UINT32_ARRAY - ); - IStore(this).registerTable(tableId, fieldLayout, defaultKeySchema, valueSchema, new string[](1), new string[](4)); + { + Schema valueSchema = SchemaEncodeHelper.encode( + SchemaType.UINT128, + SchemaType.UINT256, + SchemaType.UINT32_ARRAY, + SchemaType.UINT32_ARRAY + ); + IStore(this).registerTable(tableId, fieldLayout, defaultKeySchema, valueSchema, new string[](1), new string[](4)); + } bytes16 firstDataBytes = bytes16(0x0102030405060708090a0b0c0d0e0f10); @@ -390,9 +394,9 @@ contract StoreCoreTest is Test, StoreMock { bytes memory firstDataPacked = abi.encodePacked(firstDataBytes); - // Expect a StoreSetField event to be emitted + // Expect a StoreSpliceStaticData event to be emitted vm.expectEmit(true, true, true, true); - emit StoreSetField(tableId, keyTuple, 0, firstDataPacked); + emit StoreSpliceStaticData(tableId, keyTuple, 0, uint40(firstDataPacked.length), firstDataPacked); // Set first field IStore(this).setField(tableId, keyTuple, 0, firstDataPacked, fieldLayout); @@ -416,9 +420,15 @@ contract StoreCoreTest is Test, StoreMock { bytes memory secondDataPacked = abi.encodePacked(secondDataBytes); - // Expect a StoreSetField event to be emitted + // Expect a StoreSpliceRecord event to be emitted vm.expectEmit(true, true, true, true); - emit StoreSetField(tableId, keyTuple, 1, secondDataPacked); + emit StoreSpliceStaticData( + tableId, + keyTuple, + uint48(firstDataPacked.length), + uint40(secondDataPacked.length), + secondDataPacked + ); IStore(this).setField(tableId, keyTuple, 1, secondDataPacked, fieldLayout); @@ -463,9 +473,16 @@ contract StoreCoreTest is Test, StoreMock { fourthDataBytes = EncodeArray.encode(fourthData); } - // Expect a StoreSetField event to be emitted + // Expect a StoreSpliceRecord event to be emitted vm.expectEmit(true, true, true, true); - emit StoreSetField(tableId, keyTuple, 2, thirdDataBytes); + emit StoreSpliceDynamicData( + tableId, + keyTuple, + uint48(0), + 0, + thirdDataBytes, + PackedCounterLib.pack(thirdDataBytes.length, 0).unwrap() + ); // Set third field IStore(this).setField(tableId, keyTuple, 2, thirdDataBytes, fieldLayout); @@ -485,9 +502,16 @@ contract StoreCoreTest is Test, StoreMock { assertEq(bytes16(IStore(this).getField(tableId, keyTuple, 0, fieldLayout)), bytes16(firstDataBytes)); assertEq(bytes32(IStore(this).getField(tableId, keyTuple, 1, fieldLayout)), bytes32(secondDataBytes)); - // Expect a StoreSetField event to be emitted + // Expect a StoreSpliceRecord event to be emitted vm.expectEmit(true, true, true, true); - emit StoreSetField(tableId, keyTuple, 3, fourthDataBytes); + emit StoreSpliceDynamicData( + tableId, + keyTuple, + uint48(thirdDataBytes.length), + 0, + fourthDataBytes, + PackedCounterLib.pack(thirdDataBytes.length, fourthDataBytes.length).unwrap() + ); // Set fourth field IStore(this).setField(tableId, keyTuple, 3, fourthDataBytes, fieldLayout); @@ -507,6 +531,30 @@ contract StoreCoreTest is Test, StoreMock { abi.encodePacked(firstDataBytes, secondDataBytes, encodedLengths.unwrap(), thirdDataBytes, fourthDataBytes) ) ); + + // Set fourth field again, changing it to be equal to third field + // (non-zero deleteCount must be emitted when the array exists) + + // Expect a StoreSpliceRecord event to be emitted + vm.expectEmit(true, true, true, true); + emit StoreSpliceDynamicData( + tableId, + keyTuple, + uint48(thirdDataBytes.length), + uint40(fourthDataBytes.length), + thirdDataBytes, + PackedCounterLib.pack(thirdDataBytes.length, thirdDataBytes.length).unwrap() + ); + + // Set fourth field + IStore(this).setField(tableId, keyTuple, 3, thirdDataBytes, fieldLayout); + + // Get fourth field + loadedData = IStore(this).getField(tableId, keyTuple, 3, fieldLayout); + + // Verify loaded data is correct + assertEq(loadedData.length, thirdDataBytes.length); + assertEq(keccak256(loadedData), keccak256(thirdDataBytes)); } function testDeleteData() public { @@ -514,12 +562,14 @@ contract StoreCoreTest is Test, StoreMock { // Register table FieldLayout fieldLayout = FieldLayoutEncodeHelper.encode(16, 2); - Schema valueSchema = SchemaEncodeHelper.encode( - SchemaType.UINT128, - SchemaType.UINT32_ARRAY, - SchemaType.UINT32_ARRAY - ); - IStore(this).registerTable(tableId, fieldLayout, defaultKeySchema, valueSchema, new string[](1), new string[](3)); + { + Schema valueSchema = SchemaEncodeHelper.encode( + SchemaType.UINT128, + SchemaType.UINT32_ARRAY, + SchemaType.UINT32_ARRAY + ); + IStore(this).registerTable(tableId, fieldLayout, defaultKeySchema, valueSchema, new string[](1), new string[](3)); + } bytes16 firstDataBytes = bytes16(0x0102030405060708090a0b0c0d0e0f10); @@ -546,6 +596,8 @@ contract StoreCoreTest is Test, StoreMock { } // Concat data + bytes memory staticData = abi.encodePacked(firstDataBytes); + bytes memory dynamicData = abi.encodePacked(secondDataBytes, thirdDataBytes); bytes memory data = abi.encodePacked( firstDataBytes, encodedDynamicLength.unwrap(), @@ -558,7 +610,7 @@ contract StoreCoreTest is Test, StoreMock { keyTuple[0] = bytes32("some key"); // Set data - IStore(this).setRecord(tableId, keyTuple, data, fieldLayout); + IStore(this).setRecord(tableId, keyTuple, staticData, encodedDynamicLength, dynamicData, fieldLayout); // Get data bytes memory loadedData = IStore(this).getRecord(tableId, keyTuple, fieldLayout); @@ -647,9 +699,16 @@ contract StoreCoreTest is Test, StoreMock { } data.newSecondDataBytes = abi.encodePacked(data.secondDataBytes, data.secondDataToPush); - // Expect a StoreSetField event to be emitted + // Expect a StoreSpliceDynamicData event to be emitted vm.expectEmit(true, true, true, true); - emit StoreSetField(data.tableId, data.keyTuple, 1, data.newSecondDataBytes); + emit StoreSpliceDynamicData( + data.tableId, + data.keyTuple, + uint48(data.secondDataBytes.length), + 0, + data.secondDataToPush, + PackedCounterLib.pack(data.newSecondDataBytes.length, data.thirdDataBytes.length).unwrap() + ); // Push to second field IStore(this).pushToField(data.tableId, data.keyTuple, 1, data.secondDataToPush, fieldLayout); @@ -683,9 +742,16 @@ contract StoreCoreTest is Test, StoreMock { } data.newThirdDataBytes = abi.encodePacked(data.thirdDataBytes, data.thirdDataToPush); - // Expect a StoreSetField event to be emitted + // Expect a StoreSpliceRecord event to be emitted vm.expectEmit(true, true, true, true); - emit StoreSetField(data.tableId, data.keyTuple, 2, data.newThirdDataBytes); + emit StoreSpliceDynamicData( + data.tableId, + data.keyTuple, + uint48(data.newSecondDataBytes.length + data.thirdDataBytes.length), + 0, + data.thirdDataToPush, + PackedCounterLib.pack(data.newSecondDataBytes.length, data.newThirdDataBytes.length).unwrap() + ); // Push to third field IStore(this).pushToField(data.tableId, data.keyTuple, 2, data.thirdDataToPush, fieldLayout); @@ -786,9 +852,16 @@ contract StoreCoreTest is Test, StoreMock { data.newSecondDataBytes = abi.encodePacked(data.secondData[0], _secondDataForUpdate[0]); } - // Expect a StoreSetField event to be emitted + // Expect a StoreSpliceRecord event to be emitted vm.expectEmit(true, true, true, true); - emit StoreSetField(data.tableId, data.keyTuple, 1, data.newSecondDataBytes); + emit StoreSpliceDynamicData( + data.tableId, + data.keyTuple, + uint48(4 * 1), + 4 * 1, + data.secondDataForUpdate, + PackedCounterLib.pack(data.newSecondDataBytes.length, data.thirdDataBytes.length).unwrap() + ); // Update index 1 in second field (4 = byte length of uint32) IStore(this).updateInField(data.tableId, data.keyTuple, 1, 4 * 1, data.secondDataForUpdate, fieldLayout); @@ -824,9 +897,16 @@ contract StoreCoreTest is Test, StoreMock { ); } - // Expect a StoreSetField event to be emitted + // Expect a StoreSpliceRecord event to be emitted vm.expectEmit(true, true, true, true); - emit StoreSetField(data.tableId, data.keyTuple, 2, data.newThirdDataBytes); + emit StoreSpliceDynamicData( + data.tableId, + data.keyTuple, + uint48(data.newSecondDataBytes.length + 8 * 1), + 8 * 4, + data.thirdDataForUpdate, + PackedCounterLib.pack(data.newSecondDataBytes.length, data.newThirdDataBytes.length).unwrap() + ); // Update indexes 1,2,3,4 in third field (8 = byte length of uint64) IStore(this).updateInField(data.tableId, data.keyTuple, 2, 8 * 1, data.thirdDataForUpdate, fieldLayout); @@ -910,21 +990,21 @@ contract StoreCoreTest is Test, StoreMock { }) ); - bytes memory data = abi.encodePacked(bytes16(0x0102030405060708090a0b0c0d0e0f10)); + bytes memory staticData = abi.encodePacked(bytes16(0x0102030405060708090a0b0c0d0e0f10)); - IStore(this).setRecord(tableId, keyTuple, data, fieldLayout); + IStore(this).setRecord(tableId, keyTuple, staticData, PackedCounter.wrap(bytes32(0)), new bytes(0), fieldLayout); // Get data from indexed table - the indexer should have mirrored the data there bytes memory indexedData = IStore(this).getRecord(indexerTableId, keyTuple, fieldLayout); - assertEq(keccak256(data), keccak256(indexedData)); + assertEq(keccak256(staticData), keccak256(indexedData)); - data = abi.encodePacked(bytes16(0x1112131415161718191a1b1c1d1e1f20)); + staticData = abi.encodePacked(bytes16(0x1112131415161718191a1b1c1d1e1f20)); - IStore(this).setField(tableId, keyTuple, 0, data, fieldLayout); + IStore(this).setField(tableId, keyTuple, 0, staticData, fieldLayout); // Get data from indexed table - the indexer should have mirrored the data there indexedData = IStore(this).getRecord(indexerTableId, keyTuple, fieldLayout); - assertEq(keccak256(data), keccak256(indexedData)); + assertEq(keccak256(staticData), keccak256(indexedData)); IStore(this).deleteRecord(tableId, keyTuple, fieldLayout); @@ -978,7 +1058,7 @@ contract StoreCoreTest is Test, StoreMock { // Expect a revert when the RevertSubscriber's onBeforeSetRecord hook is called vm.expectRevert(bytes("onBeforeSetRecord")); - IStore(this).setRecord(tableId, keyTuple, data, fieldLayout); + IStore(this).setRecord(tableId, keyTuple, data, PackedCounter.wrap(bytes32(0)), new bytes(0), fieldLayout); // Expect a revert when the RevertSubscriber's onBeforeSetField hook is called vm.expectRevert(bytes("onBeforeSetField")); @@ -993,13 +1073,13 @@ contract StoreCoreTest is Test, StoreMock { // Expect a HookCalled event to be emitted when the EchoSubscriber's onBeforeSetRecord hook is called vm.expectEmit(true, true, true, true); - emit HookCalled(abi.encode(tableId, keyTuple, data, fieldLayout)); + emit HookCalled(abi.encode(tableId, keyTuple, data, PackedCounter.wrap(bytes32(0)), new bytes(0), fieldLayout)); // Expect a HookCalled event to be emitted when the EchoSubscriber's onAfterSetRecord hook is called vm.expectEmit(true, true, true, true); - emit HookCalled(abi.encode(tableId, keyTuple, data, fieldLayout)); + emit HookCalled(abi.encode(tableId, keyTuple, data, PackedCounter.wrap(bytes32(0)), new bytes(0), fieldLayout)); - IStore(this).setRecord(tableId, keyTuple, data, fieldLayout); + IStore(this).setRecord(tableId, keyTuple, data, PackedCounter.wrap(bytes32(0)), new bytes(0), fieldLayout); // Expect a HookCalled event to be emitted when the EchoSubscriber's onBeforeSetField hook is called vm.expectEmit(true, true, true, true); @@ -1059,11 +1139,11 @@ contract StoreCoreTest is Test, StoreMock { arrayData[0] = 0x01020304; bytes memory arrayDataBytes = EncodeArray.encode(arrayData); PackedCounter encodedArrayDataLength = PackedCounterLib.pack(uint40(arrayDataBytes.length)); - bytes memory dynamicData = abi.encodePacked(encodedArrayDataLength.unwrap(), arrayDataBytes); + bytes memory dynamicData = arrayDataBytes; bytes memory staticData = abi.encodePacked(bytes16(0x0102030405060708090a0b0c0d0e0f10)); - bytes memory data = abi.encodePacked(staticData, dynamicData); + bytes memory data = abi.encodePacked(staticData, encodedArrayDataLength, dynamicData); - IStore(this).setRecord(tableId, keyTuple, data, fieldLayout); + IStore(this).setRecord(tableId, keyTuple, staticData, encodedArrayDataLength, dynamicData, fieldLayout); // Get data from indexed table - the indexer should have mirrored the data there bytes memory indexedData = IStore(this).getRecord(indexerTableId, keyTuple, fieldLayout); @@ -1072,8 +1152,8 @@ contract StoreCoreTest is Test, StoreMock { // Update dynamic data arrayData[0] = 0x11121314; arrayDataBytes = EncodeArray.encode(arrayData); - dynamicData = abi.encodePacked(encodedArrayDataLength.unwrap(), arrayDataBytes); - data = abi.encodePacked(staticData, dynamicData); + dynamicData = arrayDataBytes; + data = abi.encodePacked(staticData, encodedArrayDataLength, dynamicData); IStore(this).setField(tableId, keyTuple, 1, arrayDataBytes, fieldLayout); diff --git a/packages/store/test/StoreCoreDynamic.t.sol b/packages/store/test/StoreCoreDynamic.t.sol index 28c25130a0..d5979239c3 100644 --- a/packages/store/test/StoreCoreDynamic.t.sol +++ b/packages/store/test/StoreCoreDynamic.t.sol @@ -7,6 +7,7 @@ import { SchemaType } from "@latticexyz/schema-type/src/solidity/SchemaType.sol" import { StoreCore } from "../src/StoreCore.sol"; import { SliceLib } from "../src/Slice.sol"; import { EncodeArray } from "../src/tightcoder/EncodeArray.sol"; +import { PackedCounterLib } from "../src/PackedCounter.sol"; import { FieldLayout } from "../src/FieldLayout.sol"; import { Schema } from "../src/Schema.sol"; import { StoreMock } from "../test/StoreMock.sol"; @@ -29,11 +30,11 @@ contract StoreCoreDynamicTest is Test, GasReporter, StoreMock { function popFromField( bytes32 tableId, bytes32[] calldata keyTuple, - uint8 schemaIndex, + uint8 fieldIndex, uint256 byteLengthToPop, FieldLayout fieldLayout ) public override { - StoreCore.popFromField(tableId, keyTuple, schemaIndex, byteLengthToPop, fieldLayout); + StoreCore.popFromField(tableId, keyTuple, fieldIndex, byteLengthToPop, fieldLayout); } function setUp() public { @@ -90,9 +91,16 @@ contract StoreCoreDynamicTest is Test, GasReporter, StoreMock { assertEq(SliceLib.fromBytes(dataBytes).decodeArray_uint32().length, 2); assertEq(SliceLib.fromBytes(newDataBytes).decodeArray_uint32().length, 2 - 1); - // Expect a StoreSetField event to be emitted + // Expect a StoreSpliceRecord event to be emitted vm.expectEmit(true, true, true, true); - emit StoreSetField(_tableId, _keyTuple, 1, newDataBytes); + emit StoreSpliceDynamicData( + _tableId, + _keyTuple, + uint48(secondDataBytes.length - byteLengthToPop), + uint40(byteLengthToPop), + new bytes(0), + PackedCounterLib.pack(newDataBytes.length, thirdDataBytes.length).unwrap() + ); // Pop from second field startGasReport("pop from field (cold, 1 slot, 1 uint32 item)"); @@ -129,9 +137,16 @@ contract StoreCoreDynamicTest is Test, GasReporter, StoreMock { assertEq(SliceLib.fromBytes(dataBytes).decodeArray_uint32().length, 10); assertEq(SliceLib.fromBytes(newDataBytes).decodeArray_uint32().length, 10 - 10); - // Expect a StoreSetField event to be emitted after pop + // Expect a StoreSpliceRecord event to be emitted after pop vm.expectEmit(true, true, true, true); - emit StoreSetField(_tableId, _keyTuple, 2, dataBytes); + emit StoreSpliceDynamicData( + _tableId, + _keyTuple, + uint48(secondDataBytes.length + thirdDataBytes.length - byteLengthToPop), + uint40(byteLengthToPop), + new bytes(0), + PackedCounterLib.pack(secondDataBytes.length, newDataBytes.length).unwrap() + ); // Pop from the field startGasReport("pop from field (cold, 2 slots, 10 uint32 items)"); diff --git a/packages/store/test/StoreCoreGas.t.sol b/packages/store/test/StoreCoreGas.t.sol index badbc33472..5c30514ccd 100644 --- a/packages/store/test/StoreCoreGas.t.sol +++ b/packages/store/test/StoreCoreGas.t.sol @@ -139,12 +139,13 @@ contract StoreCoreGasTest is Test, GasReporter, StoreMock { StoreCore.registerTable(tableId, fieldLayout, defaultKeySchema, valueSchema, new string[](1), new string[](4)); // Set data - bytes memory data = abi.encodePacked(bytes1(0x01), bytes2(0x0203), bytes1(0x04), bytes2(0x0506)); + bytes memory staticData = abi.encodePacked(bytes1(0x01), bytes2(0x0203), bytes1(0x04), bytes2(0x0506)); + bytes memory dynamicData = new bytes(0); bytes32[] memory keyTuple = new bytes32[](1); - keyTuple[0] = "some key"; + keyTuple[0] = keccak256("some.key"); startGasReport("set static record (1 slot)"); - StoreCore.setRecord(tableId, keyTuple, data, fieldLayout); + StoreCore.setRecord(tableId, keyTuple, staticData, PackedCounter.wrap(bytes32(0)), dynamicData, fieldLayout); endGasReport(); // Get data @@ -161,16 +162,17 @@ contract StoreCoreGasTest is Test, GasReporter, StoreMock { StoreCore.registerTable(tableId, fieldLayout, defaultKeySchema, valueSchema, new string[](1), new string[](2)); // Set data - bytes memory data = abi.encodePacked( + bytes memory staticData = abi.encodePacked( bytes16(0x0102030405060708090a0b0c0d0e0f10), bytes32(0x1112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f30) ); + bytes memory dynamicData = new bytes(0); bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = "some key"; startGasReport("set static record (2 slots)"); - StoreCore.setRecord(tableId, keyTuple, data, fieldLayout); + StoreCore.setRecord(tableId, keyTuple, staticData, PackedCounter.wrap(bytes32(0)), dynamicData, fieldLayout); endGasReport(); // Get data @@ -216,12 +218,8 @@ contract StoreCoreGasTest is Test, GasReporter, StoreMock { ); // Concat data - bytes memory data = abi.encodePacked( - firstDataBytes, - encodedDynamicLength.unwrap(), - secondDataBytes, - thirdDataBytes - ); + bytes memory staticData = abi.encodePacked(firstDataBytes); + bytes memory dynamicData = abi.encodePacked(secondDataBytes, thirdDataBytes); // Create keyTuple bytes32[] memory keyTuple = new bytes32[](1); @@ -229,7 +227,7 @@ contract StoreCoreGasTest is Test, GasReporter, StoreMock { // Set data startGasReport("set complex record with dynamic data (4 slots)"); - StoreCore.setRecord(tableId, keyTuple, data, fieldLayout); + StoreCore.setRecord(tableId, keyTuple, staticData, encodedDynamicLength, dynamicData, fieldLayout); endGasReport(); // Get data @@ -382,19 +380,15 @@ contract StoreCoreGasTest is Test, GasReporter, StoreMock { ); // Concat data - bytes memory data = abi.encodePacked( - firstDataBytes, - encodedDynamicLength.unwrap(), - secondDataBytes, - thirdDataBytes - ); + bytes memory staticData = abi.encodePacked(firstDataBytes); + bytes memory dynamicData = abi.encodePacked(secondDataBytes, thirdDataBytes); // Create keyTuple bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = "some key"; // Set data - StoreCore.setRecord(tableId, keyTuple, data, fieldLayout); + StoreCore.setRecord(tableId, keyTuple, staticData, encodedDynamicLength, dynamicData, fieldLayout); // Delete data startGasReport("delete record (complex data, 3 slots)"); @@ -632,16 +626,17 @@ contract StoreCoreGasTest is Test, GasReporter, StoreMock { ); endGasReport(); - bytes memory data = abi.encodePacked(bytes16(0x0102030405060708090a0b0c0d0e0f10)); + bytes memory staticData = abi.encodePacked(bytes16(0x0102030405060708090a0b0c0d0e0f10)); + bytes memory dynamicData = new bytes(0); startGasReport("set record on table with subscriber"); - StoreCore.setRecord(tableId, keyTuple, data, fieldLayout); + StoreCore.setRecord(tableId, keyTuple, staticData, PackedCounter.wrap(bytes32(0)), dynamicData, fieldLayout); endGasReport(); - data = abi.encodePacked(bytes16(0x1112131415161718191a1b1c1d1e1f20)); + staticData = abi.encodePacked(bytes16(0x1112131415161718191a1b1c1d1e1f20)); startGasReport("set static field on table with subscriber"); - StoreCore.setField(tableId, keyTuple, 0, data, fieldLayout); + StoreCore.setField(tableId, keyTuple, 0, staticData, fieldLayout); endGasReport(); startGasReport("delete record on table with subscriber"); @@ -688,19 +683,19 @@ contract StoreCoreGasTest is Test, GasReporter, StoreMock { arrayData[0] = 0x01020304; bytes memory arrayDataBytes = EncodeArray.encode(arrayData); PackedCounter encodedArrayDataLength = PackedCounterLib.pack(uint40(arrayDataBytes.length)); - bytes memory dynamicData = abi.encodePacked(encodedArrayDataLength.unwrap(), arrayDataBytes); + bytes memory dynamicData = arrayDataBytes; bytes memory staticData = abi.encodePacked(bytes16(0x0102030405060708090a0b0c0d0e0f10)); - bytes memory data = abi.encodePacked(staticData, dynamicData); + bytes memory data = abi.encodePacked(staticData, encodedArrayDataLength, dynamicData); startGasReport("set (dynamic) record on table with subscriber"); - StoreCore.setRecord(tableId, keyTuple, data, fieldLayout); + StoreCore.setRecord(tableId, keyTuple, staticData, encodedArrayDataLength, dynamicData, fieldLayout); endGasReport(); // Update dynamic data arrayData[0] = 0x11121314; arrayDataBytes = EncodeArray.encode(arrayData); - dynamicData = abi.encodePacked(encodedArrayDataLength.unwrap(), arrayDataBytes); - data = abi.encodePacked(staticData, dynamicData); + dynamicData = arrayDataBytes; + data = abi.encodePacked(staticData, encodedArrayDataLength, dynamicData); startGasReport("set (dynamic) field on table with subscriber"); StoreCore.setField(tableId, keyTuple, 1, arrayDataBytes, fieldLayout); diff --git a/packages/store/test/StoreHook.t.sol b/packages/store/test/StoreHook.t.sol index 56950bd351..311dcbc90a 100644 --- a/packages/store/test/StoreHook.t.sol +++ b/packages/store/test/StoreHook.t.sol @@ -11,6 +11,7 @@ import { Hook } from "../src/Hook.sol"; import { StoreHookType } from "../src/StoreHook.sol"; import { StoreHookLib } from "../src/StoreHook.sol"; import { IStoreHook } from "../src/IStore.sol"; +import { PackedCounter } from "../src/PackedCounter.sol"; import { FieldLayout } from "../src/FieldLayout.sol"; contract StoreHookTest is Test, GasReporter { @@ -21,7 +22,10 @@ contract StoreHookTest is Test, GasReporter { RevertSubscriber private revertSubscriber = new RevertSubscriber(); bytes32 private tableId = "table"; bytes32[] private key = new bytes32[](1); - bytes private data = "data"; + bytes private staticData = abi.encodePacked(bytes32(0)); + PackedCounter private encodedLengths = PackedCounter.wrap(bytes32(0)); + bytes private dynamicData = new bytes(0); + uint8 private fieldIndex = 1; FieldLayout private fieldLayout = FieldLayout.wrap(0); function testEncodeBitmap() public { @@ -278,11 +282,21 @@ contract StoreHookTest is Test, GasReporter { }) ); + // TODO temporary variable until https://github.com/foundry-rs/foundry/issues/5811 is fixed + bytes memory emptyDynamicData = new bytes(0); + vm.expectEmit(true, true, true, true); - emit HookCalled(abi.encode(tableId, key, data, fieldLayout)); + emit HookCalled(abi.encode(tableId, key, staticData, encodedLengths, emptyDynamicData, fieldLayout)); startGasReport("call an enabled hook"); if (storeHook.isEnabled(uint8(StoreHookType.BEFORE_SET_RECORD))) { - IStoreHook(storeHook.getAddress()).onBeforeSetRecord(tableId, key, data, fieldLayout); + IStoreHook(storeHook.getAddress()).onBeforeSetRecord( + tableId, + key, + staticData, + encodedLengths, + dynamicData, + fieldLayout + ); } endGasReport(); @@ -301,7 +315,14 @@ contract StoreHookTest is Test, GasReporter { // Expect the to not be called - otherwise the test will fail with a revert startGasReport("call a disabled hook"); if (revertHook.isEnabled(uint8(StoreHookType.BEFORE_SET_RECORD))) { - IStoreHook(revertHook.getAddress()).onBeforeSetRecord(tableId, key, data, fieldLayout); + IStoreHook(revertHook.getAddress()).onBeforeSetRecord( + tableId, + key, + staticData, + encodedLengths, + dynamicData, + fieldLayout + ); } endGasReport(); } diff --git a/packages/store/test/StoreMock.sol b/packages/store/test/StoreMock.sol index 96e23f6664..9b515b4af5 100644 --- a/packages/store/test/StoreMock.sol +++ b/packages/store/test/StoreMock.sol @@ -2,8 +2,9 @@ pragma solidity >=0.8.0; import { IStore, IStoreHook } from "../src/IStore.sol"; -import { Schema } from "../src/Schema.sol"; +import { PackedCounter } from "../src/PackedCounter.sol"; import { StoreCore } from "../src/StoreCore.sol"; +import { Schema } from "../src/Schema.sol"; import { FieldLayout } from "../src/FieldLayout.sol"; import { StoreRead } from "../src/StoreRead.sol"; @@ -20,55 +21,57 @@ contract StoreMock is IStore, StoreRead { function setRecord( bytes32 tableId, bytes32[] calldata keyTuple, - bytes calldata data, + bytes calldata staticData, + PackedCounter encodedLengths, + bytes calldata dynamicData, FieldLayout fieldLayout - ) public virtual { - StoreCore.setRecord(tableId, keyTuple, data, fieldLayout); + ) public { + StoreCore.setRecord(tableId, keyTuple, staticData, encodedLengths, dynamicData, fieldLayout); } // Set partial data at schema index function setField( bytes32 tableId, bytes32[] calldata keyTuple, - uint8 schemaIndex, + uint8 fieldIndex, bytes calldata data, FieldLayout fieldLayout ) public virtual { - StoreCore.setField(tableId, keyTuple, schemaIndex, data, fieldLayout); + StoreCore.setField(tableId, keyTuple, fieldIndex, data, fieldLayout); } // Push encoded items to the dynamic field at schema index function pushToField( bytes32 tableId, bytes32[] calldata keyTuple, - uint8 schemaIndex, + uint8 fieldIndex, bytes calldata dataToPush, FieldLayout fieldLayout ) public virtual { - StoreCore.pushToField(tableId, keyTuple, schemaIndex, dataToPush, fieldLayout); + StoreCore.pushToField(tableId, keyTuple, fieldIndex, dataToPush, fieldLayout); } // Pop byte length from the dynamic field at schema index function popFromField( bytes32 tableId, bytes32[] calldata keyTuple, - uint8 schemaIndex, + uint8 fieldIndex, uint256 byteLengthToPop, FieldLayout fieldLayout ) public virtual { - StoreCore.popFromField(tableId, keyTuple, schemaIndex, byteLengthToPop, fieldLayout); + StoreCore.popFromField(tableId, keyTuple, fieldIndex, byteLengthToPop, fieldLayout); } // Change encoded items within the dynamic field at schema index function updateInField( bytes32 tableId, bytes32[] calldata keyTuple, - uint8 schemaIndex, + uint8 fieldIndex, uint256 startByteIndex, bytes calldata dataToSet, FieldLayout fieldLayout ) public virtual { - StoreCore.updateInField(tableId, keyTuple, schemaIndex, startByteIndex, dataToSet, fieldLayout); + StoreCore.updateInField(tableId, keyTuple, fieldIndex, startByteIndex, dataToSet, fieldLayout); } // Set full record (including full dynamic data) @@ -80,10 +83,12 @@ contract StoreMock is IStore, StoreRead { function emitEphemeralRecord( bytes32 tableId, bytes32[] calldata keyTuple, - bytes calldata data, + bytes calldata staticData, + PackedCounter encodedLengths, + bytes calldata dynamicData, FieldLayout fieldLayout - ) public virtual { - StoreCore.emitEphemeralRecord(tableId, keyTuple, data, fieldLayout); + ) public { + StoreCore.emitEphemeralRecord(tableId, keyTuple, staticData, encodedLengths, dynamicData, fieldLayout); } function registerTable( diff --git a/packages/store/ts/codegen/ephemeral.ts b/packages/store/ts/codegen/ephemeral.ts index c7fbc9ba4f..df57892d8a 100644 --- a/packages/store/ts/codegen/ephemeral.ts +++ b/packages/store/ts/codegen/ephemeral.ts @@ -1,5 +1,6 @@ import { renderArguments, renderCommonData, renderWithStore } from "@latticexyz/common/codegen"; import { RenderTableOptions } from "./types"; +import { renderRecordData } from "./record"; export function renderEphemeralMethods(options: RenderTableOptions) { const { structName, storeArgument } = options; @@ -7,19 +8,19 @@ export function renderEphemeralMethods(options: RenderTableOptions) { let result = renderWithStore( storeArgument, - (_typedStore, _store, _commentSuffix) => ` + (_typedStore, _store, _commentSuffix, _untypedStore, _methodNamePrefix) => ` /** Emit the ephemeral event using individual values${_commentSuffix} */ - function emitEphemeral(${renderArguments([ - _typedStore, - _typedTableId, - _typedKeyArgs, - renderArguments(options.fields.map(({ name, typeWithLocation }) => `${typeWithLocation} ${name}`)), - ])}) internal { - bytes memory _data = encode(${renderArguments(options.fields.map(({ name }) => name))}); + function ${_methodNamePrefix}emitEphemeral(${renderArguments([ + _typedStore, + _typedTableId, + _typedKeyArgs, + renderArguments(options.fields.map(({ name, typeWithLocation }) => `${typeWithLocation} ${name}`)), + ])}) internal { + ${renderRecordData(options)} ${_keyTupleDefinition} - ${_store}.emitEphemeralRecord(_tableId, _keyTuple, _data, getFieldLayout()); + ${_store}.emitEphemeralRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); } ` ); @@ -27,14 +28,14 @@ export function renderEphemeralMethods(options: RenderTableOptions) { if (structName !== undefined) { result += renderWithStore( storeArgument, - (_typedStore, _store, _commentSuffix, _untypedStore) => ` + (_typedStore, _store, _commentSuffix, _untypedStore, _methodNamePrefix) => ` /** Emit the ephemeral event using the data struct${_commentSuffix} */ - function emitEphemeral(${renderArguments([ - _typedStore, - _typedTableId, - _typedKeyArgs, - `${structName} memory _table`, - ])}) internal { + function ${_methodNamePrefix}emitEphemeral(${renderArguments([ + _typedStore, + _typedTableId, + _typedKeyArgs, + `${structName} memory _table`, + ])}) internal { emitEphemeral(${renderArguments([ _untypedStore, _tableId, diff --git a/packages/store/ts/codegen/field.ts b/packages/store/ts/codegen/field.ts index 29f74789f0..2b811b864c 100644 --- a/packages/store/ts/codegen/field.ts +++ b/packages/store/ts/codegen/field.ts @@ -13,19 +13,34 @@ export function renderFieldMethods(options: RenderTableOptions) { let result = ""; for (const [schemaIndex, field] of options.fields.entries()) { + // For dynamic fields, compute the field index relative to the end of the static fields + const _typedFieldName = `${field.typeWithLocation} ${field.name}`; result += renderWithStore( storeArgument, - (_typedStore, _store, _commentSuffix) => ` + (_typedStore, _store, _commentSuffix, _untypedStore, _methodNamePrefix) => ` /** Get ${field.name}${_commentSuffix} */ - function get${field.methodNameSuffix}(${renderArguments([ + function ${_methodNamePrefix}get${field.methodNameSuffix}(${renderArguments([ _typedStore, _typedTableId, _typedKeyArgs, ])}) internal view returns (${_typedFieldName}) { ${_keyTupleDefinition} - bytes memory _blob = ${_store}.getField(_tableId, _keyTuple, ${schemaIndex}, getFieldLayout()); + ${ + field.isDynamic + ? `bytes memory _blob = ${_store}.getDynamicField( + _tableId, + _keyTuple, + ${schemaIndex - options.staticFields.length} + );` + : `bytes32 _blob = ${_store}.getStaticField( + _tableId, + _keyTuple, + ${schemaIndex}, + _fieldLayout + );` + } return ${renderDecodeFieldSingle(field)}; } ` @@ -33,16 +48,16 @@ export function renderFieldMethods(options: RenderTableOptions) { result += renderWithStore( storeArgument, - (_typedStore, _store, _commentSuffix) => ` + (_typedStore, _store, _commentSuffix, _untypedStore, _methodNamePrefix) => ` /** Set ${field.name}${_commentSuffix} */ - function set${field.methodNameSuffix}(${renderArguments([ + function ${_methodNamePrefix}set${field.methodNameSuffix}(${renderArguments([ _typedStore, _typedTableId, _typedKeyArgs, _typedFieldName, ])}) internal { ${_keyTupleDefinition} - ${_store}.setField(_tableId, _keyTuple, ${schemaIndex}, ${renderEncodeFieldSingle(field)}, getFieldLayout()); + ${_store}.setField(_tableId, _keyTuple, ${schemaIndex}, ${renderEncodeFieldSingle(field)}, _fieldLayout); } ` ); @@ -52,15 +67,15 @@ export function renderFieldMethods(options: RenderTableOptions) { result += renderWithStore( storeArgument, - (_typedStore, _store, _commentSuffix) => ` + (_typedStore, _store, _commentSuffix, _untypedStore, _methodNamePrefix) => ` /** Get the length of ${field.name}${_commentSuffix} */ - function length${field.methodNameSuffix}(${renderArguments([ + function ${_methodNamePrefix}length${field.methodNameSuffix}(${renderArguments([ _typedStore, _typedTableId, _typedKeyArgs, ])}) internal view returns (uint256) { ${_keyTupleDefinition} - uint256 _byteLength = ${_store}.getFieldLength(_tableId, _keyTuple, ${schemaIndex}, getFieldLayout()); + uint256 _byteLength = ${_store}.getFieldLength(_tableId, _keyTuple, ${schemaIndex}, _fieldLayout); unchecked { return _byteLength / ${portionData.elementLength}; } @@ -70,12 +85,12 @@ export function renderFieldMethods(options: RenderTableOptions) { result += renderWithStore( storeArgument, - (_typedStore, _store, _commentSuffix) => ` + (_typedStore, _store, _commentSuffix, _untypedStore, _methodNamePrefix) => ` /** * Get an item of ${field.name}${_commentSuffix} * (unchecked, returns invalid data if index overflows) */ - function getItem${field.methodNameSuffix}(${renderArguments([ + function ${_methodNamePrefix}getItem${field.methodNameSuffix}(${renderArguments([ _typedStore, _typedTableId, _typedKeyArgs, @@ -87,7 +102,7 @@ export function renderFieldMethods(options: RenderTableOptions) { _tableId, _keyTuple, ${schemaIndex}, - getFieldLayout(), + _fieldLayout, _index * ${portionData.elementLength}, (_index + 1) * ${portionData.elementLength} ); @@ -99,43 +114,43 @@ export function renderFieldMethods(options: RenderTableOptions) { result += renderWithStore( storeArgument, - (_typedStore, _store, _commentSuffix) => ` + (_typedStore, _store, _commentSuffix, _untypedStore, _methodNamePrefix) => ` /** Push ${portionData.title} to ${field.name}${_commentSuffix} */ - function push${field.methodNameSuffix}(${renderArguments([ + function ${_methodNamePrefix}push${field.methodNameSuffix}(${renderArguments([ _typedStore, _typedTableId, _typedKeyArgs, `${portionData.typeWithLocation} ${portionData.name}`, ])}) internal { ${_keyTupleDefinition} - ${_store}.pushToField(_tableId, _keyTuple, ${schemaIndex}, ${portionData.encoded}, getFieldLayout()); + ${_store}.pushToField(_tableId, _keyTuple, ${schemaIndex}, ${portionData.encoded}, _fieldLayout); } ` ); result += renderWithStore( storeArgument, - (_typedStore, _store, _commentSuffix) => ` + (_typedStore, _store, _commentSuffix, _untypedStore, _methodNamePrefix) => ` /** Pop ${portionData.title} from ${field.name}${_commentSuffix} */ - function pop${field.methodNameSuffix}(${renderArguments([ + function ${_methodNamePrefix}pop${field.methodNameSuffix}(${renderArguments([ _typedStore, _typedTableId, _typedKeyArgs, ])}) internal { ${_keyTupleDefinition} - ${_store}.popFromField(_tableId, _keyTuple, ${schemaIndex}, ${portionData.elementLength}, getFieldLayout()); + ${_store}.popFromField(_tableId, _keyTuple, ${schemaIndex}, ${portionData.elementLength}, _fieldLayout); } ` ); result += renderWithStore( storeArgument, - (_typedStore, _store, _commentSuffix) => ` + (_typedStore, _store, _commentSuffix, _untypedStore, _methodNamePrefix) => ` /** * Update ${portionData.title} of ${field.name}${_commentSuffix} at \`_index\` * (checked only to prevent modifying other tables; can corrupt own data if index overflows) */ - function update${field.methodNameSuffix}(${renderArguments([ + function ${_methodNamePrefix}update${field.methodNameSuffix}(${renderArguments([ _typedStore, _typedTableId, _typedKeyArgs, @@ -150,7 +165,7 @@ export function renderFieldMethods(options: RenderTableOptions) { ${schemaIndex}, _index * ${portionData.elementLength}, ${portionData.encoded}, - getFieldLayout() + _fieldLayout ); } } @@ -174,20 +189,26 @@ export function renderEncodeFieldSingle(field: RenderField) { } export function renderDecodeValueType(field: RenderType, offset: number) { - const { staticByteLength, internalTypeId } = field; + const { staticByteLength } = field; const innerSlice = `Bytes.slice${staticByteLength}(_blob, ${offset})`; + + return renderCastStaticBytesToType(field, innerSlice); +} + +function renderCastStaticBytesToType(field: RenderType, staticBytes: string) { + const { staticByteLength, internalTypeId } = field; const bits = staticByteLength * 8; let result; if (internalTypeId.match(/^uint\d{1,3}$/) || internalTypeId === "address") { - result = `${internalTypeId}(${innerSlice})`; + result = `${internalTypeId}(${staticBytes})`; } else if (internalTypeId.match(/^int\d{1,3}$/)) { - result = `${internalTypeId}(uint${bits}(${innerSlice}))`; + result = `${internalTypeId}(uint${bits}(${staticBytes}))`; } else if (internalTypeId.match(/^bytes\d{1,2}$/)) { - result = innerSlice; + result = staticBytes; } else if (internalTypeId === "bool") { - result = `_toBool(uint8(${innerSlice}))`; + result = `_toBool(uint8(${staticBytes}))`; } else { throw new Error(`Unknown value type id ${internalTypeId}`); } @@ -233,6 +254,6 @@ function renderDecodeFieldSingle(field: RenderField) { // bytes/string return `${field.typeWrap}(${field.internalTypeId}(_blob))`; } else { - return renderDecodeValueType(field, 0); + return renderCastStaticBytesToType(field, `bytes${field.staticByteLength}(_blob)`); } } diff --git a/packages/store/ts/codegen/record.ts b/packages/store/ts/codegen/record.ts index f334a01247..9b8b99bb99 100644 --- a/packages/store/ts/codegen/record.ts +++ b/packages/store/ts/codegen/record.ts @@ -1,9 +1,9 @@ import { - renderList, + RenderDynamicField, renderArguments, renderCommonData, + renderList, renderWithStore, - RenderDynamicField, } from "@latticexyz/common/codegen"; import { renderDecodeValueType } from "./field"; import { RenderTableOptions } from "./types"; @@ -14,15 +14,15 @@ export function renderRecordMethods(options: RenderTableOptions) { let result = renderWithStore( storeArgument, - (_typedStore, _store, _commentSuffix) => ` + (_typedStore, _store, _commentSuffix, _untypedStore, _methodNamePrefix) => ` /** Get the full data${_commentSuffix} */ - function get(${renderArguments([ - _typedStore, - _typedTableId, - _typedKeyArgs, - ])}) internal view returns (${renderDecodedRecord(options)}) { + function ${_methodNamePrefix}get(${renderArguments([ + _typedStore, + _typedTableId, + _typedKeyArgs, + ])}) internal view returns (${renderDecodedRecord(options)}) { ${_keyTupleDefinition} - bytes memory _blob = ${_store}.getRecord(_tableId, _keyTuple, getFieldLayout()); + bytes memory _blob = ${_store}.getRecord(_tableId, _keyTuple, _fieldLayout); return decode(_blob); } ` @@ -30,19 +30,19 @@ export function renderRecordMethods(options: RenderTableOptions) { result += renderWithStore( storeArgument, - (_typedStore, _store, _commentSuffix) => ` + (_typedStore, _store, _commentSuffix, _untypedStore, _methodNamePrefix) => ` /** Set the full data using individual values${_commentSuffix} */ - function set(${renderArguments([ - _typedStore, - _typedTableId, - _typedKeyArgs, - renderArguments(options.fields.map(({ name, typeWithLocation }) => `${typeWithLocation} ${name}`)), - ])}) internal { - bytes memory _data = encode(${renderArguments(options.fields.map(({ name }) => name))}); + function ${_methodNamePrefix}set(${renderArguments([ + _typedStore, + _typedTableId, + _typedKeyArgs, + renderArguments(options.fields.map(({ name, typeWithLocation }) => `${typeWithLocation} ${name}`)), + ])}) internal { + ${renderRecordData(options)} ${_keyTupleDefinition} - ${_store}.setRecord(_tableId, _keyTuple, _data, getFieldLayout()); + ${_store}.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); } ` ); @@ -50,14 +50,14 @@ export function renderRecordMethods(options: RenderTableOptions) { if (structName !== undefined) { result += renderWithStore( storeArgument, - (_typedStore, _store, _commentSuffix, _untypedStore) => ` + (_typedStore, _store, _commentSuffix, _untypedStore, _methodNamePrefix) => ` /** Set the full data using the data struct${_commentSuffix} */ - function set(${renderArguments([ - _typedStore, - _typedTableId, - _typedKeyArgs, - `${structName} memory _table`, - ])}) internal { + function ${_methodNamePrefix}set(${renderArguments([ + _typedStore, + _typedTableId, + _typedKeyArgs, + `${structName} memory _table`, + ])}) internal { set(${renderArguments([ _untypedStore, _tableId, @@ -74,6 +74,31 @@ export function renderRecordMethods(options: RenderTableOptions) { return result; } +export function renderRecordData(options: RenderTableOptions) { + let result = ""; + if (options.staticFields.length > 0) { + result += ` + bytes memory _staticData = encodeStatic(${renderArguments(options.staticFields.map(({ name }) => name))}); + `; + } else { + result += `bytes memory _staticData;`; + } + + if (options.dynamicFields.length > 0) { + result += ` + PackedCounter _encodedLengths = encodeLengths(${renderArguments(options.dynamicFields.map(({ name }) => name))}); + bytes memory _dynamicData = encodeDynamic(${renderArguments(options.dynamicFields.map(({ name }) => name))}); + `; + } else { + result += ` + PackedCounter _encodedLengths; + bytes memory _dynamicData; + `; + } + + return result; +} + // Renders the `decode` function that parses a bytes blob into the table data function renderDecodeFunction({ structName, fields, staticFields, dynamicFields }: RenderTableOptions) { // either set struct properties, or just variables @@ -94,63 +119,63 @@ function renderDecodeFunction({ structName, fields, staticFields, dynamicFields const totalStaticLength = staticFields.reduce((acc, { staticByteLength }) => acc + staticByteLength, 0); // decode static (optionally) and dynamic data return ` - /** - * Decode the tightly packed blob using this table's field layout. - * Undefined behaviour for invalid blobs. - */ - function decode(bytes memory _blob) internal pure returns (${renderedDecodedRecord}) { - // ${totalStaticLength} is the total byte length of static data - PackedCounter _encodedLengths = PackedCounter.wrap(Bytes.slice32(_blob, ${totalStaticLength})); - - ${renderList( - staticFields, - (field, index) => ` - ${fieldNamePrefix}${field.name} = ${renderDecodeValueType(field, staticOffsets[index])}; - ` - )} - // Store trims the blob if dynamic fields are all empty - if (_blob.length > ${totalStaticLength}) { + /** + * Decode the tightly packed blob using this table's field layout. + * Undefined behaviour for invalid blobs. + */ + function decode(bytes memory _blob) internal pure returns (${renderedDecodedRecord}) { + // ${totalStaticLength} is the total byte length of static data + PackedCounter _encodedLengths = PackedCounter.wrap(Bytes.slice32(_blob, ${totalStaticLength})); + ${renderList( - dynamicFields, - // unchecked is only dangerous if _encodedLengths (and _blob) is invalid, - // but it's assumed to be valid, and this function is meant to be mostly used internally - (field, index) => { - if (index === 0) { - return ` - // skip static data length + dynamic lengths word - uint256 _start = ${totalStaticLength + 32}; - uint256 _end; - unchecked { - _end = ${totalStaticLength + 32} + _encodedLengths.atIndex(${index}); - } - ${fieldNamePrefix}${field.name} = ${renderDecodeDynamicFieldPartial(field)}; - `; - } else { - return ` - _start = _end; - unchecked { - _end += _encodedLengths.atIndex(${index}); + staticFields, + (field, index) => ` + ${fieldNamePrefix}${field.name} = ${renderDecodeValueType(field, staticOffsets[index])}; + ` + )} + // Store trims the blob if dynamic fields are all empty + if (_blob.length > ${totalStaticLength}) { + ${renderList( + dynamicFields, + // unchecked is only dangerous if _encodedLengths (and _blob) is invalid, + // but it's assumed to be valid, and this function is meant to be mostly used internally + (field, index) => { + if (index === 0) { + return ` + // skip static data length + dynamic lengths word + uint256 _start = ${totalStaticLength + 32}; + uint256 _end; + unchecked { + _end = ${totalStaticLength + 32} + _encodedLengths.atIndex(${index}); + } + ${fieldNamePrefix}${field.name} = ${renderDecodeDynamicFieldPartial(field)}; + `; + } else { + return ` + _start = _end; + unchecked { + _end += _encodedLengths.atIndex(${index}); + } + ${fieldNamePrefix}${field.name} = ${renderDecodeDynamicFieldPartial(field)}; + `; } - ${fieldNamePrefix}${field.name} = ${renderDecodeDynamicFieldPartial(field)}; - `; } - } - )} + )} + } } - } - `; + `; } else { // decode only static data return ` - /** Decode the tightly packed blob using this table's field layout */ - function decode(bytes memory _blob) internal pure returns (${renderedDecodedRecord}) { - ${renderList( - staticFields, - (field, index) => ` - ${fieldNamePrefix}${field.name} = ${renderDecodeValueType(field, staticOffsets[index])}; - ` - )} - } + /** Decode the tightly packed blob using this table's field layout */ + function decode(bytes memory _blob) internal pure returns (${renderedDecodedRecord}) { + ${renderList( + staticFields, + (field, index) => ` + ${fieldNamePrefix}${field.name} = ${renderDecodeValueType(field, staticOffsets[index])}; + ` + )} + } `; } } diff --git a/packages/store/ts/codegen/renderFieldLayout.test.ts b/packages/store/ts/codegen/renderFieldLayout.test.ts new file mode 100644 index 0000000000..a2a170ef8d --- /dev/null +++ b/packages/store/ts/codegen/renderFieldLayout.test.ts @@ -0,0 +1,42 @@ +import { describe, expect, it } from "vitest"; +import { renderFieldLayout } from "./renderFieldLayout"; +import { RenderType } from "@latticexyz/common/codegen"; + +describe("renderFieldLayout", () => { + it("should match the FieldLayout.sol encoding", () => { + const fields = [ + { isDynamic: false, staticByteLength: 1 }, + { isDynamic: false, staticByteLength: 2 }, + { isDynamic: false, staticByteLength: 3 }, + { isDynamic: false, staticByteLength: 4 }, + { isDynamic: false, staticByteLength: 5 }, + { isDynamic: false, staticByteLength: 6 }, + { isDynamic: false, staticByteLength: 7 }, + { isDynamic: false, staticByteLength: 8 }, + { isDynamic: false, staticByteLength: 9 }, + { isDynamic: false, staticByteLength: 10 }, + { isDynamic: false, staticByteLength: 11 }, + { isDynamic: false, staticByteLength: 12 }, + { isDynamic: false, staticByteLength: 13 }, + { isDynamic: false, staticByteLength: 14 }, + { isDynamic: false, staticByteLength: 15 }, + { isDynamic: false, staticByteLength: 16 }, + { isDynamic: false, staticByteLength: 17 }, + { isDynamic: false, staticByteLength: 18 }, + { isDynamic: false, staticByteLength: 19 }, + { isDynamic: false, staticByteLength: 20 }, + { isDynamic: false, staticByteLength: 21 }, + { isDynamic: false, staticByteLength: 22 }, + { isDynamic: false, staticByteLength: 23 }, + { isDynamic: false, staticByteLength: 32 }, + { isDynamic: true, staticByteLength: 0 }, + { isDynamic: true, staticByteLength: 0 }, + { isDynamic: true, staticByteLength: 0 }, + { isDynamic: true, staticByteLength: 0 }, + ] as RenderType[]; + + expect(renderFieldLayout(fields)).toEqual( + `FieldLayout constant _fieldLayout = FieldLayout.wrap(0x013418040102030405060708090a0b0c0d0e0f10111213141516172000000000);` + ); + }); +}); diff --git a/packages/store/ts/codegen/renderFieldLayout.ts b/packages/store/ts/codegen/renderFieldLayout.ts new file mode 100644 index 0000000000..cb112a875c --- /dev/null +++ b/packages/store/ts/codegen/renderFieldLayout.ts @@ -0,0 +1,33 @@ +import { RenderType } from "@latticexyz/common/codegen"; +import { BYTE_TO_BITS, LayoutOffsets, MAX_DYNAMIC_FIELDS, MAX_TOTAL_FIELDS, WORD_LAST_INDEX } from "../constants"; + +export function renderFieldLayout(fields: RenderType[]) { + return `FieldLayout constant _fieldLayout = FieldLayout.wrap(${encodeFieldLayout(fields)});`; +} + +// Make sure this logic stays aligned with @latticexyz/store/src/FieldLayout.sol +export function encodeFieldLayout(fields: RenderType[]) { + const staticFields = fields.filter(({ isDynamic }) => !isDynamic); + const numDynamicFields = fields.length - staticFields.length; + + let fieldLayout = 0n; + let totalLength = 0; + const totalFields = fields.length; + + if (totalFields > MAX_TOTAL_FIELDS) throw new Error(`FieldLayout: invalid length ${totalFields}`); + if (numDynamicFields > MAX_DYNAMIC_FIELDS) throw new Error(`FieldLayout: invalid length ${numDynamicFields}`); + + for (let i = 0; i < staticFields.length; i++) { + const { isDynamic, staticByteLength } = fields[i]; + if (isDynamic) throw new Error(`FieldLayout: static type after dynamic type`); + + totalLength += staticByteLength; + fieldLayout |= BigInt(staticByteLength) << BigInt((WORD_LAST_INDEX - 4 - i) * BYTE_TO_BITS); + } + + fieldLayout |= BigInt(totalLength) << BigInt(LayoutOffsets.TOTAL_LENGTH); + fieldLayout |= BigInt(staticFields.length) << BigInt(LayoutOffsets.NUM_STATIC_FIELDS); + fieldLayout |= BigInt(numDynamicFields) << BigInt(LayoutOffsets.NUM_DYNAMIC_FIELDS); + + return `0x${fieldLayout.toString(16).padStart(64, "0")}`; +} diff --git a/packages/store/ts/codegen/renderTable.ts b/packages/store/ts/codegen/renderTable.ts index 8181133cfb..0d4c8e4b48 100644 --- a/packages/store/ts/codegen/renderTable.ts +++ b/packages/store/ts/codegen/renderTable.ts @@ -1,17 +1,19 @@ import { + RenderDynamicField, renderArguments, renderCommonData, renderList, - renderedSolidityHeader, renderRelativeImports, renderTableId, - renderWithStore, renderTypeHelpers, - RenderDynamicField, + renderWithStore, + renderedSolidityHeader, + RenderStaticField, } from "@latticexyz/common/codegen"; import { renderEphemeralMethods } from "./ephemeral"; import { renderEncodeFieldSingle, renderFieldMethods } from "./field"; -import { renderRecordMethods } from "./record"; +import { renderRecordData, renderRecordMethods } from "./record"; +import { renderFieldLayout } from "./renderFieldLayout"; import { RenderTableOptions } from "./types"; export function renderTable(options: RenderTableOptions) { @@ -63,6 +65,8 @@ export function renderTable(options: RenderTableOptions) { ${staticResourceData ? renderTableId(staticResourceData).tableIdDefinition : ""} + ${renderFieldLayout(fields)} + ${ !structName ? "" @@ -76,10 +80,7 @@ export function renderTable(options: RenderTableOptions) { library ${libraryName} { /** Get the table values' field layout */ function getFieldLayout() internal pure returns (FieldLayout) { - uint256[] memory _fieldLayout = new uint256[](${staticFields.length}); - ${renderList(staticFields, ({ staticByteLength }, index) => `_fieldLayout[${index}] = ${staticByteLength};`)} - - return FieldLayoutLib.encode(_fieldLayout, ${dynamicFields.length}); + return _fieldLayout; } /** Get the table's key schema */ @@ -112,12 +113,12 @@ export function renderTable(options: RenderTableOptions) { ${renderWithStore( storeArgument, - (_typedStore, _store, _commentSuffix) => ` - /** Register the table with its config${_commentSuffix} */ - function register(${renderArguments([_typedStore, _typedTableId])}) internal { - ${_store}.registerTable(_tableId, getFieldLayout(), getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); - } - ` + (_typedStore, _store, _commentSuffix, _untypedStore, _methodNamePrefix) => ` + /** Register the table with its config${_commentSuffix} */ + function ${_methodNamePrefix}register(${renderArguments([_typedStore, _typedTableId])}) internal { + ${_store}.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + } + ` )} ${withFieldMethods ? renderFieldMethods(options) : ""} @@ -126,20 +127,19 @@ export function renderTable(options: RenderTableOptions) { ${withEphemeralMethods ? renderEphemeralMethods(options) : ""} + ${renderEncodeStatic(staticFields)} + + ${renderEncodedLengths(dynamicFields)} + + ${renderEncodeDynamic(dynamicFields)} + /** Tightly pack full data using this table's field layout */ function encode(${renderArguments( options.fields.map(({ name, typeWithLocation }) => `${typeWithLocation} ${name}`) )}) internal pure returns (bytes memory) { - ${renderEncodedLengths(dynamicFields)} - return abi.encodePacked(${renderArguments([ - renderArguments(staticFields.map(({ name }) => name)), - ...(dynamicFields.length === 0 - ? [] - : [ - "_encodedLengths.unwrap()", - renderArguments(dynamicFields.map((field) => renderEncodeFieldSingle(field))), - ]), - ])}); + ${renderRecordData(options)} + + return abi.encodePacked(_staticData, _encodedLengths, _dynamicData); } /** Encode keys as a bytes32 array using this table's field layout */ @@ -152,11 +152,15 @@ export function renderTable(options: RenderTableOptions) { shouldRenderDelete ? renderWithStore( storeArgument, - (_typedStore, _store, _commentSuffix) => ` + (_typedStore, _store, _commentSuffix, _untypedStore, _methodNamePrefix) => ` /* Delete all data for given keys${_commentSuffix} */ - function deleteRecord(${renderArguments([_typedStore, _typedTableId, _typedKeyArgs])}) internal { + function ${_methodNamePrefix}deleteRecord(${renderArguments([ + _typedStore, + _typedTableId, + _typedKeyArgs, + ])}) internal { ${_keyTupleDefinition} - ${_store}.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + ${_store}.deleteRecord(_tableId, _keyTuple, _fieldLayout); } ` ) @@ -168,26 +172,54 @@ export function renderTable(options: RenderTableOptions) { `; } +function renderEncodeStatic(staticFields: RenderStaticField[]) { + if (staticFields.length === 0) return ""; + + return ` + /** Tightly pack static data using this table's schema */ + function encodeStatic(${renderArguments( + staticFields.map(({ name, typeWithLocation }) => `${typeWithLocation} ${name}`) + )}) internal pure returns (bytes memory) { + return abi.encodePacked(${renderArguments(staticFields.map(({ name }) => name))}); + } + `; +} + function renderEncodedLengths(dynamicFields: RenderDynamicField[]) { - if (dynamicFields.length > 0) { - return ` - PackedCounter _encodedLengths; - // Lengths are effectively checked during copy by 2**40 bytes exceeding gas limits - unchecked { - _encodedLengths = PackedCounterLib.pack( - ${renderArguments( - dynamicFields.map(({ name, arrayElement }) => { - if (arrayElement) { - return `${name}.length * ${arrayElement.staticByteLength}`; - } else { - return `bytes(${name}).length`; - } - }) - )} - ); + if (dynamicFields.length === 0) return ""; + + return ` + /** Tightly pack dynamic data using this table's schema */ + function encodeLengths(${renderArguments( + dynamicFields.map(({ name, typeWithLocation }) => `${typeWithLocation} ${name}`) + )}) internal pure returns (PackedCounter _encodedLengths) { + // Lengths are effectively checked during copy by 2**40 bytes exceeding gas limits + unchecked { + _encodedLengths = PackedCounterLib.pack( + ${renderArguments( + dynamicFields.map(({ name, arrayElement }) => { + if (arrayElement) { + return `${name}.length * ${arrayElement.staticByteLength}`; + } else { + return `bytes(${name}).length`; + } + }) + )} + ); + } } - `; - } else { - return ""; - } + `; +} + +function renderEncodeDynamic(dynamicFields: RenderDynamicField[]) { + if (dynamicFields.length === 0) return ""; + + return ` + /** Tightly pack dynamic data using this table's schema */ + function encodeDynamic(${renderArguments( + dynamicFields.map(({ name, typeWithLocation }) => `${typeWithLocation} ${name}`) + )}) internal pure returns (bytes memory) { + return abi.encodePacked(${renderArguments(dynamicFields.map((field) => renderEncodeFieldSingle(field)))}); + } + `; } diff --git a/packages/store/ts/constants.ts b/packages/store/ts/constants.ts new file mode 100644 index 0000000000..9c93949bf3 --- /dev/null +++ b/packages/store/ts/constants.ts @@ -0,0 +1,23 @@ +/* Shared constants */ +// Make sure these stay aligned with @latticexyz/store/src/constants.sol + +// Total byte length of an EVM word +export const WORD_SIZE = 32; +// Index of the last byte in an EVM word +export const WORD_LAST_INDEX = 31; +// Conversion for bit shifting +export const BYTE_TO_BITS = 8; + +// Schema's capacity +export const MAX_TOTAL_FIELDS = 28; +// FieldLayout's capacity +export const MAX_STATIC_FIELDS = 28; +// PackedCounter's capacity +export const MAX_DYNAMIC_FIELDS = 5; + +// FieldLayout and Schema have the same offsets for metadata +export const LayoutOffsets = { + TOTAL_LENGTH: (WORD_SIZE - 2) * BYTE_TO_BITS, + NUM_STATIC_FIELDS: (WORD_SIZE - 2 - 1) * BYTE_TO_BITS, + NUM_DYNAMIC_FIELDS: (WORD_SIZE - 2 - 1 - 1) * BYTE_TO_BITS, +}; diff --git a/packages/store/ts/storeEvents.ts b/packages/store/ts/storeEvents.ts index 77b308b8e2..5f1d1a23a9 100644 --- a/packages/store/ts/storeEvents.ts +++ b/packages/store/ts/storeEvents.ts @@ -1,6 +1,7 @@ export const storeEvents = [ - "event StoreDeleteRecord(bytes32 tableId, bytes32[] keyTuple)", - "event StoreSetField(bytes32 tableId, bytes32[] keyTuple, uint8 schemaIndex, bytes data)", - "event StoreSetRecord(bytes32 tableId, bytes32[] keyTuple, bytes data)", - "event StoreEphemeralRecord(bytes32 tableId, bytes32[] keyTuple, bytes data)", + "event StoreSetRecord(bytes32 indexed tableId, bytes32[] keyTuple, bytes staticData, bytes32 encodedLengths, bytes dynamicData)", + "event StoreSpliceStaticData(bytes32 indexed tableId, bytes32[] keyTuple, uint48 start, uint40 deleteCount, bytes data)", + "event StoreSpliceDynamicData(bytes32 indexed tableId, bytes32[] keyTuple, uint48 start, uint40 deleteCount, bytes data, bytes32 encodedLengths)", + "event StoreEphemeralRecord(bytes32 indexed tableId, bytes32[] keyTuple, bytes staticData, bytes32 encodedLengths, bytes dynamicData)", + "event StoreDeleteRecord(bytes32 indexed tableId, bytes32[] keyTuple)", ] as const; diff --git a/packages/store/ts/storeEventsAbi.test.ts b/packages/store/ts/storeEventsAbi.test.ts index 37242457f6..be25f2cd8a 100644 --- a/packages/store/ts/storeEventsAbi.test.ts +++ b/packages/store/ts/storeEventsAbi.test.ts @@ -1,22 +1,23 @@ import { describe, expect, it } from "vitest"; import { storeEventsAbi } from "./storeEventsAbi"; import IStoreAbi from "../out/IStore.sol/IStore.abi.json"; +import { AbiEvent } from "abitype"; // Make sure `storeEvents` stays in sync with Solidity definition/events describe("storeEventsAbi", () => { it("should match the store ABI", () => { - const expectedAbi = IStoreAbi.filter((item) => item.type === "event") + const expectedEvents = IStoreAbi.filter((item) => item.type === "event") as readonly AbiEvent[]; + const expectedAbi = expectedEvents .map((item) => ({ - // just return data that abitype cares about + // return data in a shape that matches abitype's parseAbi type: item.type, name: item.name, - inputs: [ - ...item.inputs.map((input) => ({ - name: input.name, - type: input.type, - })), - ], + inputs: item.inputs.map((input) => ({ + name: input.name, + type: input.type, + ...(input.indexed ? { indexed: true } : null), + })), })) .sort((a, b) => a.name.localeCompare(b.name)); diff --git a/packages/world/gas-report.json b/packages/world/gas-report.json index 30962ed0d7..35515f840a 100644 --- a/packages/world/gas-report.json +++ b/packages/world/gas-report.json @@ -3,31 +3,31 @@ "file": "test/AccessControl.t.sol", "test": "testAccessControl", "name": "AccessControl: hasAccess (cold)", - "gasUsed": 13860 + "gasUsed": 6924 }, { "file": "test/AccessControl.t.sol", "test": "testAccessControl", "name": "AccessControl: hasAccess (warm, namespace only)", - "gasUsed": 4007 + "gasUsed": 1534 }, { "file": "test/AccessControl.t.sol", "test": "testAccessControl", "name": "AccessControl: hasAccess (warm)", - "gasUsed": 7892 + "gasUsed": 2944 }, { "file": "test/AccessControl.t.sol", "test": "testRequireAccess", "name": "AccessControl: requireAccess (cold)", - "gasUsed": 13903 + "gasUsed": 6966 }, { "file": "test/AccessControl.t.sol", "test": "testRequireAccess", "name": "AccessControl: requireAccess (warm)", - "gasUsed": 7909 + "gasUsed": 2969 }, { "file": "test/AccessControl.t.sol", @@ -39,73 +39,73 @@ "file": "test/KeysInTableModule.t.sol", "test": "testInstallComposite", "name": "install keys in table module", - "gasUsed": 1519335 + "gasUsed": 1415306 }, { "file": "test/KeysInTableModule.t.sol", "test": "testInstallGas", "name": "install keys in table module", - "gasUsed": 1519335 + "gasUsed": 1415306 }, { "file": "test/KeysInTableModule.t.sol", "test": "testInstallGas", "name": "set a record on a table with keysInTableModule installed", - "gasUsed": 183388 + "gasUsed": 158959 }, { "file": "test/KeysInTableModule.t.sol", "test": "testInstallSingleton", "name": "install keys in table module", - "gasUsed": 1519335 + "gasUsed": 1415306 }, { "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookCompositeGas", "name": "install keys in table module", - "gasUsed": 1519335 + "gasUsed": 1415306 }, { "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookCompositeGas", "name": "change a composite record on a table with keysInTableModule installed", - "gasUsed": 28498 + "gasUsed": 22016 }, { "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookCompositeGas", "name": "delete a composite record on a table with keysInTableModule installed", - "gasUsed": 251457 + "gasUsed": 171520 }, { "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookGas", "name": "install keys in table module", - "gasUsed": 1519335 + "gasUsed": 1415306 }, { "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookGas", "name": "change a record on a table with keysInTableModule installed", - "gasUsed": 27221 + "gasUsed": 20738 }, { "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookGas", "name": "delete a record on a table with keysInTableModule installed", - "gasUsed": 132823 + "gasUsed": 88171 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testGetKeysWithValueGas", "name": "install keys with value module", - "gasUsed": 728346 + "gasUsed": 654597 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testGetKeysWithValueGas", "name": "Get list of keys with a given value", - "gasUsed": 7508 + "gasUsed": 5351 }, { "file": "test/KeysWithValueModule.t.sol", @@ -117,264 +117,264 @@ "file": "test/KeysWithValueModule.t.sol", "test": "testInstall", "name": "install keys with value module", - "gasUsed": 728346 + "gasUsed": 654597 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testInstall", "name": "set a record on a table with KeysWithValueModule installed", - "gasUsed": 157348 + "gasUsed": 134495 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testSetAndDeleteRecordHook", "name": "install keys with value module", - "gasUsed": 728346 + "gasUsed": 654597 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testSetAndDeleteRecordHook", "name": "change a record on a table with KeysWithValueModule installed", - "gasUsed": 126291 + "gasUsed": 104070 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testSetAndDeleteRecordHook", "name": "delete a record on a table with KeysWithValueModule installed", - "gasUsed": 49099 + "gasUsed": 32927 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testSetField", "name": "install keys with value module", - "gasUsed": 728346 + "gasUsed": 654597 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testSetField", "name": "set a field on a table with KeysWithValueModule installed", - "gasUsed": 163792 + "gasUsed": 139109 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testSetField", "name": "change a field on a table with KeysWithValueModule installed", - "gasUsed": 128551 + "gasUsed": 103868 }, { "file": "test/query.t.sol", "test": "testCombinedHasHasValueNotQuery", "name": "CombinedHasHasValueNotQuery", - "gasUsed": 141196 + "gasUsed": 101600 }, { "file": "test/query.t.sol", "test": "testCombinedHasHasValueQuery", "name": "CombinedHasHasValueQuery", - "gasUsed": 69053 + "gasUsed": 51669 }, { "file": "test/query.t.sol", "test": "testCombinedHasNotQuery", "name": "CombinedHasNotQuery", - "gasUsed": 184150 + "gasUsed": 127476 }, { "file": "test/query.t.sol", "test": "testCombinedHasQuery", "name": "CombinedHasQuery", - "gasUsed": 121994 + "gasUsed": 80997 }, { "file": "test/query.t.sol", "test": "testCombinedHasValueNotQuery", "name": "CombinedHasValueNotQuery", - "gasUsed": 117772 + "gasUsed": 82351 }, { "file": "test/query.t.sol", "test": "testCombinedHasValueQuery", "name": "CombinedHasValueQuery", - "gasUsed": 19164 + "gasUsed": 14966 }, { "file": "test/query.t.sol", "test": "testHasQuery", "name": "HasQuery", - "gasUsed": 27950 + "gasUsed": 17988 }, { "file": "test/query.t.sol", "test": "testHasQuery1000Keys", "name": "HasQuery with 1000 keys", - "gasUsed": 7068010 + "gasUsed": 5901561 }, { "file": "test/query.t.sol", "test": "testHasQuery100Keys", "name": "HasQuery with 100 keys", - "gasUsed": 672230 + "gasUsed": 550255 }, { "file": "test/query.t.sol", "test": "testHasValueQuery", "name": "HasValueQuery", - "gasUsed": 9259 + "gasUsed": 7154 }, { "file": "test/query.t.sol", "test": "testNotValueQuery", "name": "NotValueQuery", - "gasUsed": 62652 + "gasUsed": 45265 }, { "file": "test/StandardDelegationsModule.t.sol", "test": "testCallFromCallboundDelegation", "name": "register a callbound delegation", - "gasUsed": 133325 + "gasUsed": 114074 }, { "file": "test/StandardDelegationsModule.t.sol", "test": "testCallFromCallboundDelegation", "name": "call a system via a callbound delegation", - "gasUsed": 49383 + "gasUsed": 33580 }, { "file": "test/StandardDelegationsModule.t.sol", "test": "testCallFromTimeboundDelegation", "name": "register a timebound delegation", - "gasUsed": 127681 + "gasUsed": 108511 }, { "file": "test/StandardDelegationsModule.t.sol", "test": "testCallFromTimeboundDelegation", "name": "call a system via a timebound delegation", - "gasUsed": 38474 + "gasUsed": 26692 }, { "file": "test/UniqueEntityModule.t.sol", "test": "testInstall", "name": "install unique entity module", - "gasUsed": 787645 + "gasUsed": 678986 }, { "file": "test/UniqueEntityModule.t.sol", "test": "testInstall", "name": "get a unique entity nonce (non-root module)", - "gasUsed": 67456 + "gasUsed": 51179 }, { "file": "test/UniqueEntityModule.t.sol", "test": "testInstallRoot", "name": "installRoot unique entity module", - "gasUsed": 771167 + "gasUsed": 669045 }, { "file": "test/UniqueEntityModule.t.sol", "test": "testInstallRoot", "name": "get a unique entity nonce (root module)", - "gasUsed": 67456 + "gasUsed": 51179 }, { "file": "test/World.t.sol", "test": "testCall", "name": "call a system via the World", - "gasUsed": 19564 + "gasUsed": 12288 }, { "file": "test/World.t.sol", "test": "testCallFromUnlimitedDelegation", "name": "register an unlimited delegation", - "gasUsed": 59044 + "gasUsed": 50259 }, { "file": "test/World.t.sol", "test": "testCallFromUnlimitedDelegation", "name": "call a system via an unlimited delegation", - "gasUsed": 19950 + "gasUsed": 12682 }, { "file": "test/World.t.sol", "test": "testDeleteRecord", "name": "Delete record", - "gasUsed": 13886 + "gasUsed": 8846 }, { "file": "test/World.t.sol", "test": "testPushToField", "name": "Push data to the table", - "gasUsed": 93261 + "gasUsed": 88225 }, { "file": "test/World.t.sol", "test": "testRegisterFallbackSystem", "name": "Register a fallback system", - "gasUsed": 75414 + "gasUsed": 58887 }, { "file": "test/World.t.sol", "test": "testRegisterFallbackSystem", "name": "Register a root fallback system", - "gasUsed": 68663 + "gasUsed": 52205 }, { "file": "test/World.t.sol", "test": "testRegisterFunctionSelector", "name": "Register a function selector", - "gasUsed": 96008 + "gasUsed": 79481 }, { "file": "test/World.t.sol", "test": "testRegisterNamespace", "name": "Register a new namespace", - "gasUsed": 146378 + "gasUsed": 122752 }, { "file": "test/World.t.sol", "test": "testRegisterRootFunctionSelector", "name": "Register a root function selector", - "gasUsed": 90581 + "gasUsed": 74123 }, { "file": "test/World.t.sol", "test": "testRegisterTable", "name": "Register a new table in the namespace", - "gasUsed": 685344 + "gasUsed": 641673 }, { "file": "test/World.t.sol", "test": "testSetField", "name": "Write data to a table field", - "gasUsed": 41899 + "gasUsed": 37131 }, { "file": "test/World.t.sol", "test": "testSetRecord", "name": "Write data to the table", - "gasUsed": 41344 + "gasUsed": 35125 }, { "file": "test/WorldDynamicUpdate.t.sol", "test": "testPopFromField", "name": "pop 1 address (cold)", - "gasUsed": 32940 + "gasUsed": 25429 }, { "file": "test/WorldDynamicUpdate.t.sol", "test": "testPopFromField", "name": "pop 1 address (warm)", - "gasUsed": 19729 + "gasUsed": 14541 }, { "file": "test/WorldDynamicUpdate.t.sol", "test": "testUpdateInField", "name": "updateInField 1 item (cold)", - "gasUsed": 35373 + "gasUsed": 28784 }, { "file": "test/WorldDynamicUpdate.t.sol", "test": "testUpdateInField", "name": "updateInField 1 item (warm)", - "gasUsed": 22578 + "gasUsed": 17989 } ] diff --git a/packages/world/src/AccessControl.sol b/packages/world/src/AccessControl.sol index ca637f7c3f..d99fd415ef 100644 --- a/packages/world/src/AccessControl.sol +++ b/packages/world/src/AccessControl.sol @@ -16,8 +16,8 @@ library AccessControl { function hasAccess(bytes32 resourceSelector, address caller) internal view returns (bool) { return address(this) == caller || // First check if the World is calling itself - ResourceAccess.get(resourceSelector.getNamespace(), caller) || // Then check access based on the namespace - ResourceAccess.get(resourceSelector, caller); // If caller has no namespace access, check access on the name + ResourceAccess._get(resourceSelector.getNamespace(), caller) || // Then check access based on the namespace + ResourceAccess._get(resourceSelector, caller); // If caller has no namespace access, check access on the name } /** @@ -36,7 +36,7 @@ library AccessControl { * Reverts with AccessDenied if the check fails. */ function requireOwner(bytes32 resourceSelector, address caller) internal view { - if (NamespaceOwner.get(resourceSelector.getNamespace()) != caller) { + if (NamespaceOwner._get(resourceSelector.getNamespace()) != caller) { revert IWorldErrors.AccessDenied(resourceSelector.toString(), caller); } } diff --git a/packages/world/src/SystemCall.sol b/packages/world/src/SystemCall.sol index d5bdf7f53f..f2863b00c4 100644 --- a/packages/world/src/SystemCall.sol +++ b/packages/world/src/SystemCall.sol @@ -34,7 +34,7 @@ library SystemCall { bytes memory funcSelectorAndArgs ) internal returns (bool success, bytes memory data) { // Load the system data - (address systemAddress, bool publicAccess) = Systems.get(resourceSelector); + (address systemAddress, bool publicAccess) = Systems._get(resourceSelector); // Check if the system exists if (systemAddress == address(0)) revert IWorldErrors.ResourceNotFound(resourceSelector.toString()); @@ -45,8 +45,8 @@ library SystemCall { // If the msg.value is non-zero, update the namespace's balance if (value > 0) { bytes16 namespace = resourceSelector.getNamespace(); - uint256 currentBalance = Balances.get(namespace); - Balances.set(namespace, currentBalance + value); + uint256 currentBalance = Balances._get(namespace); + Balances._set(namespace, currentBalance + value); } // Call the system and forward any return data @@ -76,7 +76,7 @@ library SystemCall { uint256 value ) internal returns (bool success, bytes memory data) { // Get system hooks - bytes21[] memory hooks = SystemHooks.get(resourceSelector); + bytes21[] memory hooks = SystemHooks._get(resourceSelector); // Call onBeforeCallSystem hooks (before calling the system) for (uint256 i; i < hooks.length; i++) { diff --git a/packages/world/src/World.sol b/packages/world/src/World.sol index 1c80c16249..bc3895dd1e 100644 --- a/packages/world/src/World.sol +++ b/packages/world/src/World.sol @@ -6,6 +6,8 @@ import { StoreCore } from "@latticexyz/store/src/StoreCore.sol"; import { IStoreData } from "@latticexyz/store/src/IStore.sol"; import { StoreCore } from "@latticexyz/store/src/StoreCore.sol"; import { Bytes } from "@latticexyz/store/src/Bytes.sol"; +import { Schema } from "@latticexyz/store/src/Schema.sol"; +import { PackedCounter } from "@latticexyz/store/src/PackedCounter.sol"; import { FieldLayout } from "@latticexyz/store/src/FieldLayout.sol"; import { System } from "./System.sol"; @@ -52,7 +54,7 @@ contract World is StoreRead, IStoreData, IWorldKernel { } // The World can only be initialized once - if (InstalledModules.get(CORE_MODULE_NAME, keccak256("")) != address(0)) { + if (InstalledModules._get(CORE_MODULE_NAME, keccak256("")) != address(0)) { revert WorldAlreadyInitialized(); } @@ -82,7 +84,7 @@ contract World is StoreRead, IStoreData, IWorldKernel { }); // Register the module in the InstalledModules table - InstalledModules.set(module.getName(), keccak256(args), address(module)); + InstalledModules._set(module.getName(), keccak256(args), address(module)); } /************************************************************************ @@ -98,14 +100,16 @@ contract World is StoreRead, IStoreData, IWorldKernel { function setRecord( bytes32 tableId, bytes32[] calldata keyTuple, - bytes calldata data, + bytes calldata staticData, + PackedCounter encodedLengths, + bytes calldata dynamicData, FieldLayout fieldLayout ) public virtual { // Require access to the namespace or name AccessControl.requireAccess(tableId, msg.sender); // Set the record - StoreCore.setRecord(tableId, keyTuple, data, fieldLayout); + StoreCore.setRecord(tableId, keyTuple, staticData, encodedLengths, dynamicData, fieldLayout); } /** @@ -115,7 +119,7 @@ contract World is StoreRead, IStoreData, IWorldKernel { function setField( bytes32 tableId, bytes32[] calldata keyTuple, - uint8 schemaIndex, + uint8 fieldIndex, bytes calldata data, FieldLayout fieldLayout ) public virtual { @@ -123,7 +127,7 @@ contract World is StoreRead, IStoreData, IWorldKernel { AccessControl.requireAccess(tableId, msg.sender); // Set the field - StoreCore.setField(tableId, keyTuple, schemaIndex, data, fieldLayout); + StoreCore.setField(tableId, keyTuple, fieldIndex, data, fieldLayout); } /** @@ -133,7 +137,7 @@ contract World is StoreRead, IStoreData, IWorldKernel { function pushToField( bytes32 tableId, bytes32[] calldata keyTuple, - uint8 schemaIndex, + uint8 fieldIndex, bytes calldata dataToPush, FieldLayout fieldLayout ) public virtual { @@ -141,7 +145,7 @@ contract World is StoreRead, IStoreData, IWorldKernel { AccessControl.requireAccess(tableId, msg.sender); // Push to the field - StoreCore.pushToField(tableId, keyTuple, schemaIndex, dataToPush, fieldLayout); + StoreCore.pushToField(tableId, keyTuple, fieldIndex, dataToPush, fieldLayout); } /** @@ -151,7 +155,7 @@ contract World is StoreRead, IStoreData, IWorldKernel { function popFromField( bytes32 tableId, bytes32[] calldata keyTuple, - uint8 schemaIndex, + uint8 fieldIndex, uint256 byteLengthToPop, FieldLayout fieldLayout ) public virtual { @@ -159,7 +163,7 @@ contract World is StoreRead, IStoreData, IWorldKernel { AccessControl.requireAccess(tableId, msg.sender); // Push to the field - StoreCore.popFromField(tableId, keyTuple, schemaIndex, byteLengthToPop, fieldLayout); + StoreCore.popFromField(tableId, keyTuple, fieldIndex, byteLengthToPop, fieldLayout); } /** @@ -169,7 +173,7 @@ contract World is StoreRead, IStoreData, IWorldKernel { function updateInField( bytes32 tableId, bytes32[] calldata keyTuple, - uint8 schemaIndex, + uint8 fieldIndex, uint256 startByteIndex, bytes calldata dataToSet, FieldLayout fieldLayout @@ -178,7 +182,7 @@ contract World is StoreRead, IStoreData, IWorldKernel { AccessControl.requireAccess(tableId, msg.sender); // Update data in the field - StoreCore.updateInField(tableId, keyTuple, schemaIndex, startByteIndex, dataToSet, fieldLayout); + StoreCore.updateInField(tableId, keyTuple, fieldIndex, startByteIndex, dataToSet, fieldLayout); } /** @@ -225,7 +229,7 @@ contract World is StoreRead, IStoreData, IWorldKernel { } // Check if there is an explicit authorization for this caller to perform actions on behalf of the delegator - Delegation explicitDelegation = Delegation.wrap(Delegations.get({ delegator: delegator, delegatee: msg.sender })); + Delegation explicitDelegation = Delegation.wrap(Delegations._get({ delegator: delegator, delegatee: msg.sender })); if (explicitDelegation.verify(delegator, msg.sender, resourceSelector, funcSelectorAndArgs)) { // forward the call as `delegator` @@ -233,7 +237,7 @@ contract World is StoreRead, IStoreData, IWorldKernel { } // Check if the delegator has a fallback delegation control set - Delegation fallbackDelegation = Delegation.wrap(Delegations.get({ delegator: delegator, delegatee: address(0) })); + Delegation fallbackDelegation = Delegation.wrap(Delegations._get({ delegator: delegator, delegatee: address(0) })); if (fallbackDelegation.verify(delegator, msg.sender, resourceSelector, funcSelectorAndArgs)) { // forward the call with `from` as `msgSender` return SystemCall.callWithHooksOrRevert(delegator, resourceSelector, funcSelectorAndArgs, msg.value); @@ -252,15 +256,15 @@ contract World is StoreRead, IStoreData, IWorldKernel { * ETH sent to the World without calldata is added to the root namespace's balance */ receive() external payable { - uint256 rootBalance = Balances.get(ROOT_NAMESPACE); - Balances.set(ROOT_NAMESPACE, rootBalance + msg.value); + uint256 rootBalance = Balances._get(ROOT_NAMESPACE); + Balances._set(ROOT_NAMESPACE, rootBalance + msg.value); } /** * Fallback function to call registered function selectors */ fallback() external payable { - (bytes32 resourceSelector, bytes4 systemFunctionSelector) = FunctionSelectors.get(msg.sig); + (bytes32 resourceSelector, bytes4 systemFunctionSelector) = FunctionSelectors._get(msg.sig); if (resourceSelector == 0) revert FunctionSelectorNotFound(msg.sig); diff --git a/packages/world/src/modules/core/CoreModule.sol b/packages/world/src/modules/core/CoreModule.sol index aaf34ff19e..a9712b4576 100644 --- a/packages/world/src/modules/core/CoreModule.sol +++ b/packages/world/src/modules/core/CoreModule.sol @@ -76,9 +76,9 @@ contract CoreModule is Module { SystemRegistry.register(); ResourceType.register(); - NamespaceOwner.set(ROOT_NAMESPACE, _msgSender()); - ResourceAccess.set(ROOT_NAMESPACE, _msgSender(), true); - ResourceType.set(ROOT_NAMESPACE, Resource.NAMESPACE); + NamespaceOwner._set(ROOT_NAMESPACE, _msgSender()); + ResourceAccess._set(ROOT_NAMESPACE, _msgSender(), true); + ResourceType._set(ROOT_NAMESPACE, Resource.NAMESPACE); } /** diff --git a/packages/world/src/modules/core/implementations/AccessManagementSystem.sol b/packages/world/src/modules/core/implementations/AccessManagementSystem.sol index c29d693ac0..cff47dd2db 100644 --- a/packages/world/src/modules/core/implementations/AccessManagementSystem.sol +++ b/packages/world/src/modules/core/implementations/AccessManagementSystem.sol @@ -22,7 +22,7 @@ contract AccessManagementSystem is System { AccessControl.requireOwner(resourceSelector, _msgSender()); // Grant access to the given resource - ResourceAccess.set(resourceSelector, grantee, true); + ResourceAccess._set(resourceSelector, grantee, true); } /** @@ -34,7 +34,7 @@ contract AccessManagementSystem is System { AccessControl.requireOwner(resourceSelector, _msgSender()); // Revoke access from the given resource - ResourceAccess.deleteRecord(resourceSelector, grantee); + ResourceAccess._deleteRecord(resourceSelector, grantee); } /** @@ -47,12 +47,12 @@ contract AccessManagementSystem is System { AccessControl.requireOwner(namespace, _msgSender()); // Set namespace new owner - NamespaceOwner.set(namespace, newOwner); + NamespaceOwner._set(namespace, newOwner); // Revoke access from old owner - ResourceAccess.deleteRecord(namespace, _msgSender()); + ResourceAccess._deleteRecord(namespace, _msgSender()); // Grant access to new owner - ResourceAccess.set(namespace, newOwner, true); + ResourceAccess._set(namespace, newOwner, true); } } diff --git a/packages/world/src/modules/core/implementations/BalanceTransferSystem.sol b/packages/world/src/modules/core/implementations/BalanceTransferSystem.sol index d57cc82a6c..9f0fdde1cd 100644 --- a/packages/world/src/modules/core/implementations/BalanceTransferSystem.sol +++ b/packages/world/src/modules/core/implementations/BalanceTransferSystem.sol @@ -20,14 +20,14 @@ contract BalanceTransferSystem is System, IWorldErrors { AccessControl.requireAccess(fromNamespace, _msgSender()); // Get current namespace balance - uint256 balance = Balances.get(fromNamespace); + uint256 balance = Balances._get(fromNamespace); // Require the balance balance to be greater or equal to the amount to transfer if (amount > balance) revert InsufficientBalance(balance, amount); // Update the balances - Balances.set(fromNamespace, balance - amount); - Balances.set(toNamespace, Balances.get(toNamespace) + amount); + Balances._set(fromNamespace, balance - amount); + Balances._set(toNamespace, Balances._get(toNamespace) + amount); } /** @@ -38,13 +38,13 @@ contract BalanceTransferSystem is System, IWorldErrors { AccessControl.requireAccess(fromNamespace, _msgSender()); // Get current namespace balance - uint256 balance = Balances.get(fromNamespace); + uint256 balance = Balances._get(fromNamespace); // Require the balance balance to be greater or equal to the amount to transfer if (amount > balance) revert InsufficientBalance(balance, amount); // Update the balances - Balances.set(fromNamespace, balance - amount); + Balances._set(fromNamespace, balance - amount); // Transfer the balance to the given address, revert on failure (bool success, bytes memory data) = payable(toAddress).call{ value: amount }(""); diff --git a/packages/world/src/modules/core/implementations/EphemeralRecordSystem.sol b/packages/world/src/modules/core/implementations/EphemeralRecordSystem.sol index 5a41ebf30b..35f2309d8c 100644 --- a/packages/world/src/modules/core/implementations/EphemeralRecordSystem.sol +++ b/packages/world/src/modules/core/implementations/EphemeralRecordSystem.sol @@ -2,12 +2,12 @@ pragma solidity >=0.8.0; import { IStoreEphemeral } from "@latticexyz/store/src/IStore.sol"; +import { StoreCore } from "@latticexyz/store/src/StoreCore.sol"; +import { PackedCounter } from "@latticexyz/store/src/PackedCounter.sol"; import { FieldLayout } from "@latticexyz/store/src/FieldLayout.sol"; -import { IModule } from "../../../interfaces/IModule.sol"; import { System } from "../../../System.sol"; import { ResourceSelector } from "../../../ResourceSelector.sol"; import { AccessControl } from "../../../AccessControl.sol"; -import { StoreCore } from "@latticexyz/store/src/StoreCore.sol"; contract EphemeralRecordSystem is IStoreEphemeral, System { using ResourceSelector for bytes32; @@ -19,13 +19,15 @@ contract EphemeralRecordSystem is IStoreEphemeral, System { function emitEphemeralRecord( bytes32 resourceSelector, bytes32[] calldata keyTuple, - bytes calldata data, + bytes calldata staticData, + PackedCounter encodedLengths, + bytes calldata dynamicData, FieldLayout fieldLayout ) public virtual { // Require access to the namespace or name AccessControl.requireAccess(resourceSelector, msg.sender); // Set the record - StoreCore.emitEphemeralRecord(resourceSelector, keyTuple, data, fieldLayout); + StoreCore.emitEphemeralRecord(resourceSelector, keyTuple, staticData, encodedLengths, dynamicData, fieldLayout); } } diff --git a/packages/world/src/modules/core/implementations/ModuleInstallationSystem.sol b/packages/world/src/modules/core/implementations/ModuleInstallationSystem.sol index ebd21ed856..80a004975a 100644 --- a/packages/world/src/modules/core/implementations/ModuleInstallationSystem.sol +++ b/packages/world/src/modules/core/implementations/ModuleInstallationSystem.sol @@ -28,6 +28,6 @@ contract ModuleInstallationSystem is System { }); // Register the module in the InstalledModules table - InstalledModules.set(module.getName(), keccak256(args), address(module)); + InstalledModules._set(module.getName(), keccak256(args), address(module)); } } diff --git a/packages/world/src/modules/core/implementations/StoreRegistrationSystem.sol b/packages/world/src/modules/core/implementations/StoreRegistrationSystem.sol index 00a5d3017c..eacfff6087 100644 --- a/packages/world/src/modules/core/implementations/StoreRegistrationSystem.sol +++ b/packages/world/src/modules/core/implementations/StoreRegistrationSystem.sol @@ -49,10 +49,10 @@ contract StoreRegistrationSystem is System, IWorldErrors { // If the namespace doesn't exist yet, register it bytes16 namespace = resourceSelector.getNamespace(); - if (ResourceType.get(namespace) == Resource.NONE) { + if (ResourceType._get(namespace) == Resource.NONE) { // We can't call IBaseWorld(this).registerNamespace directly because it would be handled like // an external call, so msg.sender would be the address of the World contract - (address systemAddress, ) = Systems.get(ResourceSelector.from(ROOT_NAMESPACE, CORE_SYSTEM_NAME)); + (address systemAddress, ) = Systems._get(ResourceSelector.from(ROOT_NAMESPACE, CORE_SYSTEM_NAME)); WorldContextProvider.delegatecallWithContextOrRevert({ msgSender: _msgSender(), msgValue: 0, @@ -65,12 +65,12 @@ contract StoreRegistrationSystem is System, IWorldErrors { } // Require no resource to exist at this selector yet - if (ResourceType.get(resourceSelector) != Resource.NONE) { + if (ResourceType._get(resourceSelector) != Resource.NONE) { revert ResourceExists(resourceSelector.toString()); } // Store the table resource type - ResourceType.set(resourceSelector, Resource.TABLE); + ResourceType._set(resourceSelector, Resource.TABLE); // Register the table StoreCore.registerTable(resourceSelector, fieldLayout, keySchema, valueSchema, keyNames, fieldNames); diff --git a/packages/world/src/modules/core/implementations/WorldRegistrationSystem.sol b/packages/world/src/modules/core/implementations/WorldRegistrationSystem.sol index 2d018ae0fb..3502e5bd6c 100644 --- a/packages/world/src/modules/core/implementations/WorldRegistrationSystem.sol +++ b/packages/world/src/modules/core/implementations/WorldRegistrationSystem.sol @@ -39,16 +39,16 @@ contract WorldRegistrationSystem is System, IWorldErrors { bytes32 resourceSelector = ResourceSelector.from(namespace); // Require namespace to not exist yet - if (ResourceType.get(namespace) != Resource.NONE) revert ResourceExists(resourceSelector.toString()); + if (ResourceType._get(namespace) != Resource.NONE) revert ResourceExists(resourceSelector.toString()); // Register namespace resource - ResourceType.set(namespace, Resource.NAMESPACE); + ResourceType._set(namespace, Resource.NAMESPACE); // Register caller as the namespace owner - NamespaceOwner.set(namespace, _msgSender()); + NamespaceOwner._set(namespace, _msgSender()); // Give caller access to the new namespace - ResourceAccess.set(resourceSelector, _msgSender(), true); + ResourceAccess._set(resourceSelector, _msgSender(), true); } /** @@ -97,7 +97,7 @@ contract WorldRegistrationSystem is System, IWorldErrors { if (resourceSelector.getName() == ROOT_NAME) revert InvalidSelector(resourceSelector.toString()); // Require this system to not be registered at a different resource selector yet - bytes32 existingResourceSelector = SystemRegistry.get(address(system)); + bytes32 existingResourceSelector = SystemRegistry._get(address(system)); if (existingResourceSelector != 0 && existingResourceSelector != resourceSelector) { revert SystemExists(address(system)); } @@ -105,38 +105,38 @@ contract WorldRegistrationSystem is System, IWorldErrors { // If the namespace doesn't exist yet, register it // otherwise require caller to own the namespace bytes16 namespace = resourceSelector.getNamespace(); - if (ResourceType.get(namespace) == Resource.NONE) registerNamespace(namespace); + if (ResourceType._get(namespace) == Resource.NONE) registerNamespace(namespace); else AccessControl.requireOwner(namespace, _msgSender()); // Require no resource other than a system to exist at this selector yet - Resource resourceType = ResourceType.get(resourceSelector); + Resource resourceType = ResourceType._get(resourceSelector); if (resourceType != Resource.NONE && resourceType != Resource.SYSTEM) { revert ResourceExists(resourceSelector.toString()); } // Check if a system already exists at this resource selector - address existingSystem = Systems.getSystem(resourceSelector); + address existingSystem = Systems._getSystem(resourceSelector); // If there is an existing system with this resource selector, remove it if (existingSystem != address(0)) { // Remove the existing system from the system registry - SystemRegistry.deleteRecord(existingSystem); + SystemRegistry._deleteRecord(existingSystem); // Remove the existing system's access to its namespace - ResourceAccess.deleteRecord(namespace, existingSystem); + ResourceAccess._deleteRecord(namespace, existingSystem); } else { // Otherwise, this is a new system, so register its resource type - ResourceType.set(resourceSelector, Resource.SYSTEM); + ResourceType._set(resourceSelector, Resource.SYSTEM); } // Systems = mapping from resourceSelector to system address and publicAccess - Systems.set(resourceSelector, address(system), publicAccess); + Systems._set(resourceSelector, address(system), publicAccess); // SystemRegistry = mapping from system address to resourceSelector - SystemRegistry.set(address(system), resourceSelector); + SystemRegistry._set(address(system), resourceSelector); // Grant the system access to its namespace - ResourceAccess.set(namespace, address(system), true); + ResourceAccess._set(namespace, address(system), true); } /** @@ -162,7 +162,7 @@ contract WorldRegistrationSystem is System, IWorldErrors { ); // Require the function selector to be globally unique - bytes32 existingResourceSelector = FunctionSelectors.getResourceSelector(worldFunctionSelector); + bytes32 existingResourceSelector = FunctionSelectors._getResourceSelector(worldFunctionSelector); if (existingResourceSelector != 0) revert FunctionSelectorExists(worldFunctionSelector); @@ -171,7 +171,7 @@ contract WorldRegistrationSystem is System, IWorldErrors { bytes4 systemFunctionSelector = systemFunctionSignature.length == 0 ? bytes4(0) // Save gas by storing 0x0 for empty function signatures (= fallback function) : bytes4(keccak256(systemFunctionSignature)); - FunctionSelectors.set(worldFunctionSelector, resourceSelector, systemFunctionSelector); + FunctionSelectors._set(worldFunctionSelector, resourceSelector, systemFunctionSelector); } /** @@ -190,12 +190,12 @@ contract WorldRegistrationSystem is System, IWorldErrors { AccessControl.requireOwner(ROOT_NAMESPACE, _msgSender()); // Require the function selector to be globally unique - bytes32 existingResourceSelector = FunctionSelectors.getResourceSelector(worldFunctionSelector); + bytes32 existingResourceSelector = FunctionSelectors._getResourceSelector(worldFunctionSelector); if (existingResourceSelector != 0) revert FunctionSelectorExists(worldFunctionSelector); // Register the function selector - FunctionSelectors.set(worldFunctionSelector, resourceSelector, systemFunctionSelector); + FunctionSelectors._set(worldFunctionSelector, resourceSelector, systemFunctionSelector); return worldFunctionSelector; } @@ -214,7 +214,7 @@ contract WorldRegistrationSystem is System, IWorldErrors { // If the delegation is not unlimited... if (delegationControlId != UNLIMITED_DELEGATION && initFuncSelectorAndArgs.length > 0) { // Require the delegationControl contract to implement the IDelegationControl interface - (address delegationControl, ) = Systems.get(delegationControlId); + (address delegationControl, ) = Systems._get(delegationControlId); requireInterface(delegationControl, DELEGATION_CONTROL_INTERFACE_ID); // Call the delegation control contract's init function diff --git a/packages/world/src/modules/core/tables/Balances.sol b/packages/world/src/modules/core/tables/Balances.sol index 76f03b4f3c..e87221fb49 100644 --- a/packages/world/src/modules/core/tables/Balances.sol +++ b/packages/world/src/modules/core/tables/Balances.sol @@ -21,13 +21,14 @@ import { PackedCounter, PackedCounterLib } from "@latticexyz/store/src/PackedCou bytes32 constant _tableId = bytes32(abi.encodePacked(bytes16(""), bytes16("Balances"))); bytes32 constant BalancesTableId = _tableId; +FieldLayout constant _fieldLayout = FieldLayout.wrap( + 0x0020010020000000000000000000000000000000000000000000000000000000 +); + library Balances { /** Get the table values' field layout */ function getFieldLayout() internal pure returns (FieldLayout) { - uint256[] memory _fieldLayout = new uint256[](1); - _fieldLayout[0] = 32; - - return FieldLayoutLib.encode(_fieldLayout, 0); + return _fieldLayout; } /** Get the table's key schema */ @@ -60,19 +61,17 @@ library Balances { /** Register the table with its config */ function register() internal { - StoreSwitch.registerTable( - _tableId, - getFieldLayout(), - getKeySchema(), - getValueSchema(), - getKeyNames(), - getFieldNames() - ); + StoreSwitch.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + } + + /** Register the table with its config */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Register the table with its config (using the specified store) */ function register(IStore _store) internal { - _store.registerTable(_tableId, getFieldLayout(), getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + _store.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Get balance */ @@ -80,8 +79,17 @@ library Balances { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(namespace); - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (uint256(Bytes.slice32(_blob, 0))); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint256(bytes32(_blob))); + } + + /** Get balance */ + function _get(bytes16 namespace) internal view returns (uint256 balance) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(namespace); + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint256(bytes32(_blob))); } /** Get balance (using the specified store) */ @@ -89,8 +97,8 @@ library Balances { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(namespace); - bytes memory _blob = _store.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (uint256(Bytes.slice32(_blob, 0))); + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint256(bytes32(_blob))); } /** Set balance */ @@ -98,7 +106,15 @@ library Balances { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(namespace); - StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((balance)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((balance)), _fieldLayout); + } + + /** Set balance */ + function _set(bytes16 namespace, uint256 balance) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(namespace); + + StoreCore.setField(_tableId, _keyTuple, 0, abi.encodePacked((balance)), _fieldLayout); } /** Set balance (using the specified store) */ @@ -106,12 +122,22 @@ library Balances { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(namespace); - _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((balance)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((balance)), _fieldLayout); + } + + /** Tightly pack static data using this table's schema */ + function encodeStatic(uint256 balance) internal pure returns (bytes memory) { + return abi.encodePacked(balance); } /** Tightly pack full data using this table's field layout */ function encode(uint256 balance) internal pure returns (bytes memory) { - return abi.encodePacked(balance); + bytes memory _staticData = encodeStatic(balance); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; + + return abi.encodePacked(_staticData, _encodedLengths, _dynamicData); } /** Encode keys as a bytes32 array using this table's field layout */ @@ -127,7 +153,15 @@ library Balances { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(namespace); - StoreSwitch.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + StoreSwitch.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /* Delete all data for given keys */ + function _deleteRecord(bytes16 namespace) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(namespace); + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); } /* Delete all data for given keys (using the specified store) */ @@ -135,6 +169,6 @@ library Balances { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(namespace); - _store.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + _store.deleteRecord(_tableId, _keyTuple, _fieldLayout); } } diff --git a/packages/world/src/modules/core/tables/FunctionSelectors.sol b/packages/world/src/modules/core/tables/FunctionSelectors.sol index e3023b3273..036335865a 100644 --- a/packages/world/src/modules/core/tables/FunctionSelectors.sol +++ b/packages/world/src/modules/core/tables/FunctionSelectors.sol @@ -21,14 +21,14 @@ import { PackedCounter, PackedCounterLib } from "@latticexyz/store/src/PackedCou bytes32 constant _tableId = bytes32(abi.encodePacked(bytes16(""), bytes16("FunctionSelector"))); bytes32 constant FunctionSelectorsTableId = _tableId; +FieldLayout constant _fieldLayout = FieldLayout.wrap( + 0x0024020020040000000000000000000000000000000000000000000000000000 +); + library FunctionSelectors { /** Get the table values' field layout */ function getFieldLayout() internal pure returns (FieldLayout) { - uint256[] memory _fieldLayout = new uint256[](2); - _fieldLayout[0] = 32; - _fieldLayout[1] = 4; - - return FieldLayoutLib.encode(_fieldLayout, 0); + return _fieldLayout; } /** Get the table's key schema */ @@ -63,19 +63,17 @@ library FunctionSelectors { /** Register the table with its config */ function register() internal { - StoreSwitch.registerTable( - _tableId, - getFieldLayout(), - getKeySchema(), - getValueSchema(), - getKeyNames(), - getFieldNames() - ); + StoreSwitch.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + } + + /** Register the table with its config */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Register the table with its config (using the specified store) */ function register(IStore _store) internal { - _store.registerTable(_tableId, getFieldLayout(), getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + _store.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Get resourceSelector */ @@ -83,8 +81,17 @@ library FunctionSelectors { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(functionSelector); - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (Bytes.slice32(_blob, 0)); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (bytes32(_blob)); + } + + /** Get resourceSelector */ + function _getResourceSelector(bytes4 functionSelector) internal view returns (bytes32 resourceSelector) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(functionSelector); + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (bytes32(_blob)); } /** Get resourceSelector (using the specified store) */ @@ -95,8 +102,8 @@ library FunctionSelectors { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(functionSelector); - bytes memory _blob = _store.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (Bytes.slice32(_blob, 0)); + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (bytes32(_blob)); } /** Set resourceSelector */ @@ -104,7 +111,15 @@ library FunctionSelectors { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(functionSelector); - StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((resourceSelector)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((resourceSelector)), _fieldLayout); + } + + /** Set resourceSelector */ + function _setResourceSelector(bytes4 functionSelector, bytes32 resourceSelector) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(functionSelector); + + StoreCore.setField(_tableId, _keyTuple, 0, abi.encodePacked((resourceSelector)), _fieldLayout); } /** Set resourceSelector (using the specified store) */ @@ -112,7 +127,7 @@ library FunctionSelectors { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(functionSelector); - _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((resourceSelector)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((resourceSelector)), _fieldLayout); } /** Get systemFunctionSelector */ @@ -120,8 +135,17 @@ library FunctionSelectors { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(functionSelector); - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 1, getFieldLayout()); - return (Bytes.slice4(_blob, 0)); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 1, _fieldLayout); + return (bytes4(_blob)); + } + + /** Get systemFunctionSelector */ + function _getSystemFunctionSelector(bytes4 functionSelector) internal view returns (bytes4 systemFunctionSelector) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(functionSelector); + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 1, _fieldLayout); + return (bytes4(_blob)); } /** Get systemFunctionSelector (using the specified store) */ @@ -132,8 +156,8 @@ library FunctionSelectors { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(functionSelector); - bytes memory _blob = _store.getField(_tableId, _keyTuple, 1, getFieldLayout()); - return (Bytes.slice4(_blob, 0)); + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 1, _fieldLayout); + return (bytes4(_blob)); } /** Set systemFunctionSelector */ @@ -141,7 +165,15 @@ library FunctionSelectors { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(functionSelector); - StoreSwitch.setField(_tableId, _keyTuple, 1, abi.encodePacked((systemFunctionSelector)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 1, abi.encodePacked((systemFunctionSelector)), _fieldLayout); + } + + /** Set systemFunctionSelector */ + function _setSystemFunctionSelector(bytes4 functionSelector, bytes4 systemFunctionSelector) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(functionSelector); + + StoreCore.setField(_tableId, _keyTuple, 1, abi.encodePacked((systemFunctionSelector)), _fieldLayout); } /** Set systemFunctionSelector (using the specified store) */ @@ -149,7 +181,7 @@ library FunctionSelectors { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(functionSelector); - _store.setField(_tableId, _keyTuple, 1, abi.encodePacked((systemFunctionSelector)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 1, abi.encodePacked((systemFunctionSelector)), _fieldLayout); } /** Get the full data */ @@ -159,7 +191,18 @@ library FunctionSelectors { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(functionSelector); - bytes memory _blob = StoreSwitch.getRecord(_tableId, _keyTuple, getFieldLayout()); + bytes memory _blob = StoreSwitch.getRecord(_tableId, _keyTuple, _fieldLayout); + return decode(_blob); + } + + /** Get the full data */ + function _get( + bytes4 functionSelector + ) internal view returns (bytes32 resourceSelector, bytes4 systemFunctionSelector) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(functionSelector); + + bytes memory _blob = StoreCore.getRecord(_tableId, _keyTuple, _fieldLayout); return decode(_blob); } @@ -171,18 +214,34 @@ library FunctionSelectors { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(functionSelector); - bytes memory _blob = _store.getRecord(_tableId, _keyTuple, getFieldLayout()); + bytes memory _blob = _store.getRecord(_tableId, _keyTuple, _fieldLayout); return decode(_blob); } /** Set the full data using individual values */ function set(bytes4 functionSelector, bytes32 resourceSelector, bytes4 systemFunctionSelector) internal { - bytes memory _data = encode(resourceSelector, systemFunctionSelector); + bytes memory _staticData = encodeStatic(resourceSelector, systemFunctionSelector); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; + + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(functionSelector); + + StoreSwitch.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); + } + + /** Set the full data using individual values */ + function _set(bytes4 functionSelector, bytes32 resourceSelector, bytes4 systemFunctionSelector) internal { + bytes memory _staticData = encodeStatic(resourceSelector, systemFunctionSelector); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(functionSelector); - StoreSwitch.setRecord(_tableId, _keyTuple, _data, getFieldLayout()); + StoreCore.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); } /** Set the full data using individual values (using the specified store) */ @@ -192,12 +251,15 @@ library FunctionSelectors { bytes32 resourceSelector, bytes4 systemFunctionSelector ) internal { - bytes memory _data = encode(resourceSelector, systemFunctionSelector); + bytes memory _staticData = encodeStatic(resourceSelector, systemFunctionSelector); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(functionSelector); - _store.setRecord(_tableId, _keyTuple, _data, getFieldLayout()); + _store.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); } /** Decode the tightly packed blob using this table's field layout */ @@ -207,9 +269,19 @@ library FunctionSelectors { systemFunctionSelector = (Bytes.slice4(_blob, 32)); } + /** Tightly pack static data using this table's schema */ + function encodeStatic(bytes32 resourceSelector, bytes4 systemFunctionSelector) internal pure returns (bytes memory) { + return abi.encodePacked(resourceSelector, systemFunctionSelector); + } + /** Tightly pack full data using this table's field layout */ function encode(bytes32 resourceSelector, bytes4 systemFunctionSelector) internal pure returns (bytes memory) { - return abi.encodePacked(resourceSelector, systemFunctionSelector); + bytes memory _staticData = encodeStatic(resourceSelector, systemFunctionSelector); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; + + return abi.encodePacked(_staticData, _encodedLengths, _dynamicData); } /** Encode keys as a bytes32 array using this table's field layout */ @@ -225,7 +297,15 @@ library FunctionSelectors { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(functionSelector); - StoreSwitch.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + StoreSwitch.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /* Delete all data for given keys */ + function _deleteRecord(bytes4 functionSelector) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(functionSelector); + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); } /* Delete all data for given keys (using the specified store) */ @@ -233,6 +313,6 @@ library FunctionSelectors { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(functionSelector); - _store.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + _store.deleteRecord(_tableId, _keyTuple, _fieldLayout); } } diff --git a/packages/world/src/modules/core/tables/ResourceType.sol b/packages/world/src/modules/core/tables/ResourceType.sol index 45ba98b6fb..0fb6a1c293 100644 --- a/packages/world/src/modules/core/tables/ResourceType.sol +++ b/packages/world/src/modules/core/tables/ResourceType.sol @@ -24,13 +24,14 @@ import { Resource } from "./../../../Types.sol"; bytes32 constant _tableId = bytes32(abi.encodePacked(bytes16(""), bytes16("ResourceType"))); bytes32 constant ResourceTypeTableId = _tableId; +FieldLayout constant _fieldLayout = FieldLayout.wrap( + 0x0001010001000000000000000000000000000000000000000000000000000000 +); + library ResourceType { /** Get the table values' field layout */ function getFieldLayout() internal pure returns (FieldLayout) { - uint256[] memory _fieldLayout = new uint256[](1); - _fieldLayout[0] = 1; - - return FieldLayoutLib.encode(_fieldLayout, 0); + return _fieldLayout; } /** Get the table's key schema */ @@ -63,19 +64,17 @@ library ResourceType { /** Register the table with its config */ function register() internal { - StoreSwitch.registerTable( - _tableId, - getFieldLayout(), - getKeySchema(), - getValueSchema(), - getKeyNames(), - getFieldNames() - ); + StoreSwitch.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + } + + /** Register the table with its config */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Register the table with its config (using the specified store) */ function register(IStore _store) internal { - _store.registerTable(_tableId, getFieldLayout(), getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + _store.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Get resourceType */ @@ -83,8 +82,17 @@ library ResourceType { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = resourceSelector; - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return Resource(uint8(Bytes.slice1(_blob, 0))); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return Resource(uint8(bytes1(_blob))); + } + + /** Get resourceType */ + function _get(bytes32 resourceSelector) internal view returns (Resource resourceType) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = resourceSelector; + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return Resource(uint8(bytes1(_blob))); } /** Get resourceType (using the specified store) */ @@ -92,8 +100,8 @@ library ResourceType { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = resourceSelector; - bytes memory _blob = _store.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return Resource(uint8(Bytes.slice1(_blob, 0))); + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return Resource(uint8(bytes1(_blob))); } /** Set resourceType */ @@ -101,7 +109,15 @@ library ResourceType { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = resourceSelector; - StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked(uint8(resourceType)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked(uint8(resourceType)), _fieldLayout); + } + + /** Set resourceType */ + function _set(bytes32 resourceSelector, Resource resourceType) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = resourceSelector; + + StoreCore.setField(_tableId, _keyTuple, 0, abi.encodePacked(uint8(resourceType)), _fieldLayout); } /** Set resourceType (using the specified store) */ @@ -109,12 +125,22 @@ library ResourceType { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = resourceSelector; - _store.setField(_tableId, _keyTuple, 0, abi.encodePacked(uint8(resourceType)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 0, abi.encodePacked(uint8(resourceType)), _fieldLayout); + } + + /** Tightly pack static data using this table's schema */ + function encodeStatic(Resource resourceType) internal pure returns (bytes memory) { + return abi.encodePacked(resourceType); } /** Tightly pack full data using this table's field layout */ function encode(Resource resourceType) internal pure returns (bytes memory) { - return abi.encodePacked(resourceType); + bytes memory _staticData = encodeStatic(resourceType); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; + + return abi.encodePacked(_staticData, _encodedLengths, _dynamicData); } /** Encode keys as a bytes32 array using this table's field layout */ @@ -130,7 +156,15 @@ library ResourceType { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = resourceSelector; - StoreSwitch.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + StoreSwitch.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /* Delete all data for given keys */ + function _deleteRecord(bytes32 resourceSelector) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = resourceSelector; + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); } /* Delete all data for given keys (using the specified store) */ @@ -138,6 +172,6 @@ library ResourceType { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = resourceSelector; - _store.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + _store.deleteRecord(_tableId, _keyTuple, _fieldLayout); } } diff --git a/packages/world/src/modules/core/tables/SystemHooks.sol b/packages/world/src/modules/core/tables/SystemHooks.sol index 0bb1d03e8a..a322e22ced 100644 --- a/packages/world/src/modules/core/tables/SystemHooks.sol +++ b/packages/world/src/modules/core/tables/SystemHooks.sol @@ -21,12 +21,14 @@ import { PackedCounter, PackedCounterLib } from "@latticexyz/store/src/PackedCou bytes32 constant _tableId = bytes32(abi.encodePacked(bytes16(""), bytes16("SystemHooks"))); bytes32 constant SystemHooksTableId = _tableId; +FieldLayout constant _fieldLayout = FieldLayout.wrap( + 0x0000000100000000000000000000000000000000000000000000000000000000 +); + library SystemHooks { /** Get the table values' field layout */ function getFieldLayout() internal pure returns (FieldLayout) { - uint256[] memory _fieldLayout = new uint256[](0); - - return FieldLayoutLib.encode(_fieldLayout, 1); + return _fieldLayout; } /** Get the table's key schema */ @@ -59,19 +61,17 @@ library SystemHooks { /** Register the table with its config */ function register() internal { - StoreSwitch.registerTable( - _tableId, - getFieldLayout(), - getKeySchema(), - getValueSchema(), - getKeyNames(), - getFieldNames() - ); + StoreSwitch.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + } + + /** Register the table with its config */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Register the table with its config (using the specified store) */ function register(IStore _store) internal { - _store.registerTable(_tableId, getFieldLayout(), getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + _store.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Get value */ @@ -79,7 +79,16 @@ library SystemHooks { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = resourceSelector; - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 0, getFieldLayout()); + bytes memory _blob = StoreSwitch.getDynamicField(_tableId, _keyTuple, 0); + return (SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_bytes21()); + } + + /** Get value */ + function _get(bytes32 resourceSelector) internal view returns (bytes21[] memory value) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = resourceSelector; + + bytes memory _blob = StoreCore.getDynamicField(_tableId, _keyTuple, 0); return (SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_bytes21()); } @@ -88,7 +97,7 @@ library SystemHooks { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = resourceSelector; - bytes memory _blob = _store.getField(_tableId, _keyTuple, 0, getFieldLayout()); + bytes memory _blob = _store.getDynamicField(_tableId, _keyTuple, 0); return (SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_bytes21()); } @@ -97,7 +106,15 @@ library SystemHooks { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = resourceSelector; - StoreSwitch.setField(_tableId, _keyTuple, 0, EncodeArray.encode((value)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 0, EncodeArray.encode((value)), _fieldLayout); + } + + /** Set value */ + function _set(bytes32 resourceSelector, bytes21[] memory value) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = resourceSelector; + + StoreCore.setField(_tableId, _keyTuple, 0, EncodeArray.encode((value)), _fieldLayout); } /** Set value (using the specified store) */ @@ -105,7 +122,7 @@ library SystemHooks { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = resourceSelector; - _store.setField(_tableId, _keyTuple, 0, EncodeArray.encode((value)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 0, EncodeArray.encode((value)), _fieldLayout); } /** Get the length of value */ @@ -113,7 +130,18 @@ library SystemHooks { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = resourceSelector; - uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 0, getFieldLayout()); + uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 0, _fieldLayout); + unchecked { + return _byteLength / 21; + } + } + + /** Get the length of value */ + function _length(bytes32 resourceSelector) internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = resourceSelector; + + uint256 _byteLength = StoreCore.getFieldLength(_tableId, _keyTuple, 0, _fieldLayout); unchecked { return _byteLength / 21; } @@ -124,7 +152,7 @@ library SystemHooks { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = resourceSelector; - uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 0, getFieldLayout()); + uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 0, _fieldLayout); unchecked { return _byteLength / 21; } @@ -143,32 +171,46 @@ library SystemHooks { _tableId, _keyTuple, 0, - getFieldLayout(), + _fieldLayout, _index * 21, (_index + 1) * 21 ); - return (Bytes.slice21(_blob, 0)); + return (bytes21(_blob)); } } /** - * Get an item of value (using the specified store) + * Get an item of value * (unchecked, returns invalid data if index overflows) */ - function getItem(IStore _store, bytes32 resourceSelector, uint256 _index) internal view returns (bytes21) { + function _getItem(bytes32 resourceSelector, uint256 _index) internal view returns (bytes21) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = resourceSelector; unchecked { - bytes memory _blob = _store.getFieldSlice( + bytes memory _blob = StoreCore.getFieldSlice( _tableId, _keyTuple, 0, - getFieldLayout(), + _fieldLayout, _index * 21, (_index + 1) * 21 ); - return (Bytes.slice21(_blob, 0)); + return (bytes21(_blob)); + } + } + + /** + * Get an item of value (using the specified store) + * (unchecked, returns invalid data if index overflows) + */ + function getItem(IStore _store, bytes32 resourceSelector, uint256 _index) internal view returns (bytes21) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = resourceSelector; + + unchecked { + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 0, _fieldLayout, _index * 21, (_index + 1) * 21); + return (bytes21(_blob)); } } @@ -177,7 +219,15 @@ library SystemHooks { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = resourceSelector; - StoreSwitch.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), getFieldLayout()); + StoreSwitch.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), _fieldLayout); + } + + /** Push an element to value */ + function _push(bytes32 resourceSelector, bytes21 _element) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = resourceSelector; + + StoreCore.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), _fieldLayout); } /** Push an element to value (using the specified store) */ @@ -185,7 +235,7 @@ library SystemHooks { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = resourceSelector; - _store.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), getFieldLayout()); + _store.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), _fieldLayout); } /** Pop an element from value */ @@ -193,7 +243,15 @@ library SystemHooks { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = resourceSelector; - StoreSwitch.popFromField(_tableId, _keyTuple, 0, 21, getFieldLayout()); + StoreSwitch.popFromField(_tableId, _keyTuple, 0, 21, _fieldLayout); + } + + /** Pop an element from value */ + function _pop(bytes32 resourceSelector) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = resourceSelector; + + StoreCore.popFromField(_tableId, _keyTuple, 0, 21, _fieldLayout); } /** Pop an element from value (using the specified store) */ @@ -201,7 +259,7 @@ library SystemHooks { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = resourceSelector; - _store.popFromField(_tableId, _keyTuple, 0, 21, getFieldLayout()); + _store.popFromField(_tableId, _keyTuple, 0, 21, _fieldLayout); } /** @@ -213,7 +271,20 @@ library SystemHooks { _keyTuple[0] = resourceSelector; unchecked { - StoreSwitch.updateInField(_tableId, _keyTuple, 0, _index * 21, abi.encodePacked((_element)), getFieldLayout()); + StoreSwitch.updateInField(_tableId, _keyTuple, 0, _index * 21, abi.encodePacked((_element)), _fieldLayout); + } + } + + /** + * Update an element of value at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ + function _update(bytes32 resourceSelector, uint256 _index, bytes21 _element) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = resourceSelector; + + unchecked { + StoreCore.updateInField(_tableId, _keyTuple, 0, _index * 21, abi.encodePacked((_element)), _fieldLayout); } } @@ -226,19 +297,30 @@ library SystemHooks { _keyTuple[0] = resourceSelector; unchecked { - _store.updateInField(_tableId, _keyTuple, 0, _index * 21, abi.encodePacked((_element)), getFieldLayout()); + _store.updateInField(_tableId, _keyTuple, 0, _index * 21, abi.encodePacked((_element)), _fieldLayout); } } - /** Tightly pack full data using this table's field layout */ - function encode(bytes21[] memory value) internal pure returns (bytes memory) { - PackedCounter _encodedLengths; + /** Tightly pack dynamic data using this table's schema */ + function encodeLengths(bytes21[] memory value) internal pure returns (PackedCounter _encodedLengths) { // Lengths are effectively checked during copy by 2**40 bytes exceeding gas limits unchecked { _encodedLengths = PackedCounterLib.pack(value.length * 21); } + } - return abi.encodePacked(_encodedLengths.unwrap(), EncodeArray.encode((value))); + /** Tightly pack dynamic data using this table's schema */ + function encodeDynamic(bytes21[] memory value) internal pure returns (bytes memory) { + return abi.encodePacked(EncodeArray.encode((value))); + } + + /** Tightly pack full data using this table's field layout */ + function encode(bytes21[] memory value) internal pure returns (bytes memory) { + bytes memory _staticData; + PackedCounter _encodedLengths = encodeLengths(value); + bytes memory _dynamicData = encodeDynamic(value); + + return abi.encodePacked(_staticData, _encodedLengths, _dynamicData); } /** Encode keys as a bytes32 array using this table's field layout */ @@ -254,7 +336,15 @@ library SystemHooks { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = resourceSelector; - StoreSwitch.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + StoreSwitch.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /* Delete all data for given keys */ + function _deleteRecord(bytes32 resourceSelector) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = resourceSelector; + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); } /* Delete all data for given keys (using the specified store) */ @@ -262,6 +352,6 @@ library SystemHooks { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = resourceSelector; - _store.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + _store.deleteRecord(_tableId, _keyTuple, _fieldLayout); } } diff --git a/packages/world/src/modules/core/tables/SystemRegistry.sol b/packages/world/src/modules/core/tables/SystemRegistry.sol index 1bae5fb67b..42f7311644 100644 --- a/packages/world/src/modules/core/tables/SystemRegistry.sol +++ b/packages/world/src/modules/core/tables/SystemRegistry.sol @@ -21,13 +21,14 @@ import { PackedCounter, PackedCounterLib } from "@latticexyz/store/src/PackedCou bytes32 constant _tableId = bytes32(abi.encodePacked(bytes16(""), bytes16("SystemRegistry"))); bytes32 constant SystemRegistryTableId = _tableId; +FieldLayout constant _fieldLayout = FieldLayout.wrap( + 0x0020010020000000000000000000000000000000000000000000000000000000 +); + library SystemRegistry { /** Get the table values' field layout */ function getFieldLayout() internal pure returns (FieldLayout) { - uint256[] memory _fieldLayout = new uint256[](1); - _fieldLayout[0] = 32; - - return FieldLayoutLib.encode(_fieldLayout, 0); + return _fieldLayout; } /** Get the table's key schema */ @@ -60,19 +61,17 @@ library SystemRegistry { /** Register the table with its config */ function register() internal { - StoreSwitch.registerTable( - _tableId, - getFieldLayout(), - getKeySchema(), - getValueSchema(), - getKeyNames(), - getFieldNames() - ); + StoreSwitch.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + } + + /** Register the table with its config */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Register the table with its config (using the specified store) */ function register(IStore _store) internal { - _store.registerTable(_tableId, getFieldLayout(), getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + _store.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Get resourceSelector */ @@ -80,8 +79,17 @@ library SystemRegistry { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(uint256(uint160(system))); - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (Bytes.slice32(_blob, 0)); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (bytes32(_blob)); + } + + /** Get resourceSelector */ + function _get(address system) internal view returns (bytes32 resourceSelector) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(uint160(system))); + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (bytes32(_blob)); } /** Get resourceSelector (using the specified store) */ @@ -89,8 +97,8 @@ library SystemRegistry { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(uint256(uint160(system))); - bytes memory _blob = _store.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (Bytes.slice32(_blob, 0)); + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (bytes32(_blob)); } /** Set resourceSelector */ @@ -98,7 +106,15 @@ library SystemRegistry { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(uint256(uint160(system))); - StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((resourceSelector)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((resourceSelector)), _fieldLayout); + } + + /** Set resourceSelector */ + function _set(address system, bytes32 resourceSelector) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(uint160(system))); + + StoreCore.setField(_tableId, _keyTuple, 0, abi.encodePacked((resourceSelector)), _fieldLayout); } /** Set resourceSelector (using the specified store) */ @@ -106,12 +122,22 @@ library SystemRegistry { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(uint256(uint160(system))); - _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((resourceSelector)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((resourceSelector)), _fieldLayout); + } + + /** Tightly pack static data using this table's schema */ + function encodeStatic(bytes32 resourceSelector) internal pure returns (bytes memory) { + return abi.encodePacked(resourceSelector); } /** Tightly pack full data using this table's field layout */ function encode(bytes32 resourceSelector) internal pure returns (bytes memory) { - return abi.encodePacked(resourceSelector); + bytes memory _staticData = encodeStatic(resourceSelector); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; + + return abi.encodePacked(_staticData, _encodedLengths, _dynamicData); } /** Encode keys as a bytes32 array using this table's field layout */ @@ -127,7 +153,15 @@ library SystemRegistry { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(uint256(uint160(system))); - StoreSwitch.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + StoreSwitch.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /* Delete all data for given keys */ + function _deleteRecord(address system) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(uint160(system))); + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); } /* Delete all data for given keys (using the specified store) */ @@ -135,6 +169,6 @@ library SystemRegistry { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(uint256(uint160(system))); - _store.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + _store.deleteRecord(_tableId, _keyTuple, _fieldLayout); } } diff --git a/packages/world/src/modules/core/tables/Systems.sol b/packages/world/src/modules/core/tables/Systems.sol index fa6622d37e..f60421fce5 100644 --- a/packages/world/src/modules/core/tables/Systems.sol +++ b/packages/world/src/modules/core/tables/Systems.sol @@ -21,14 +21,14 @@ import { PackedCounter, PackedCounterLib } from "@latticexyz/store/src/PackedCou bytes32 constant _tableId = bytes32(abi.encodePacked(bytes16(""), bytes16("Systems"))); bytes32 constant SystemsTableId = _tableId; +FieldLayout constant _fieldLayout = FieldLayout.wrap( + 0x0015020014010000000000000000000000000000000000000000000000000000 +); + library Systems { /** Get the table values' field layout */ function getFieldLayout() internal pure returns (FieldLayout) { - uint256[] memory _fieldLayout = new uint256[](2); - _fieldLayout[0] = 20; - _fieldLayout[1] = 1; - - return FieldLayoutLib.encode(_fieldLayout, 0); + return _fieldLayout; } /** Get the table's key schema */ @@ -63,19 +63,17 @@ library Systems { /** Register the table with its config */ function register() internal { - StoreSwitch.registerTable( - _tableId, - getFieldLayout(), - getKeySchema(), - getValueSchema(), - getKeyNames(), - getFieldNames() - ); + StoreSwitch.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + } + + /** Register the table with its config */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Register the table with its config (using the specified store) */ function register(IStore _store) internal { - _store.registerTable(_tableId, getFieldLayout(), getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + _store.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Get system */ @@ -83,8 +81,17 @@ library Systems { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = resourceSelector; - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (address(Bytes.slice20(_blob, 0))); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (address(bytes20(_blob))); + } + + /** Get system */ + function _getSystem(bytes32 resourceSelector) internal view returns (address system) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = resourceSelector; + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (address(bytes20(_blob))); } /** Get system (using the specified store) */ @@ -92,8 +99,8 @@ library Systems { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = resourceSelector; - bytes memory _blob = _store.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (address(Bytes.slice20(_blob, 0))); + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (address(bytes20(_blob))); } /** Set system */ @@ -101,7 +108,15 @@ library Systems { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = resourceSelector; - StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((system)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((system)), _fieldLayout); + } + + /** Set system */ + function _setSystem(bytes32 resourceSelector, address system) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = resourceSelector; + + StoreCore.setField(_tableId, _keyTuple, 0, abi.encodePacked((system)), _fieldLayout); } /** Set system (using the specified store) */ @@ -109,7 +124,7 @@ library Systems { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = resourceSelector; - _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((system)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((system)), _fieldLayout); } /** Get publicAccess */ @@ -117,8 +132,17 @@ library Systems { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = resourceSelector; - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 1, getFieldLayout()); - return (_toBool(uint8(Bytes.slice1(_blob, 0)))); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 1, _fieldLayout); + return (_toBool(uint8(bytes1(_blob)))); + } + + /** Get publicAccess */ + function _getPublicAccess(bytes32 resourceSelector) internal view returns (bool publicAccess) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = resourceSelector; + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 1, _fieldLayout); + return (_toBool(uint8(bytes1(_blob)))); } /** Get publicAccess (using the specified store) */ @@ -126,8 +150,8 @@ library Systems { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = resourceSelector; - bytes memory _blob = _store.getField(_tableId, _keyTuple, 1, getFieldLayout()); - return (_toBool(uint8(Bytes.slice1(_blob, 0)))); + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 1, _fieldLayout); + return (_toBool(uint8(bytes1(_blob)))); } /** Set publicAccess */ @@ -135,7 +159,15 @@ library Systems { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = resourceSelector; - StoreSwitch.setField(_tableId, _keyTuple, 1, abi.encodePacked((publicAccess)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 1, abi.encodePacked((publicAccess)), _fieldLayout); + } + + /** Set publicAccess */ + function _setPublicAccess(bytes32 resourceSelector, bool publicAccess) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = resourceSelector; + + StoreCore.setField(_tableId, _keyTuple, 1, abi.encodePacked((publicAccess)), _fieldLayout); } /** Set publicAccess (using the specified store) */ @@ -143,7 +175,7 @@ library Systems { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = resourceSelector; - _store.setField(_tableId, _keyTuple, 1, abi.encodePacked((publicAccess)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 1, abi.encodePacked((publicAccess)), _fieldLayout); } /** Get the full data */ @@ -151,7 +183,16 @@ library Systems { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = resourceSelector; - bytes memory _blob = StoreSwitch.getRecord(_tableId, _keyTuple, getFieldLayout()); + bytes memory _blob = StoreSwitch.getRecord(_tableId, _keyTuple, _fieldLayout); + return decode(_blob); + } + + /** Get the full data */ + function _get(bytes32 resourceSelector) internal view returns (address system, bool publicAccess) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = resourceSelector; + + bytes memory _blob = StoreCore.getRecord(_tableId, _keyTuple, _fieldLayout); return decode(_blob); } @@ -160,28 +201,47 @@ library Systems { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = resourceSelector; - bytes memory _blob = _store.getRecord(_tableId, _keyTuple, getFieldLayout()); + bytes memory _blob = _store.getRecord(_tableId, _keyTuple, _fieldLayout); return decode(_blob); } /** Set the full data using individual values */ function set(bytes32 resourceSelector, address system, bool publicAccess) internal { - bytes memory _data = encode(system, publicAccess); + bytes memory _staticData = encodeStatic(system, publicAccess); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = resourceSelector; - StoreSwitch.setRecord(_tableId, _keyTuple, _data, getFieldLayout()); + StoreSwitch.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); + } + + /** Set the full data using individual values */ + function _set(bytes32 resourceSelector, address system, bool publicAccess) internal { + bytes memory _staticData = encodeStatic(system, publicAccess); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; + + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = resourceSelector; + + StoreCore.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); } /** Set the full data using individual values (using the specified store) */ function set(IStore _store, bytes32 resourceSelector, address system, bool publicAccess) internal { - bytes memory _data = encode(system, publicAccess); + bytes memory _staticData = encodeStatic(system, publicAccess); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = resourceSelector; - _store.setRecord(_tableId, _keyTuple, _data, getFieldLayout()); + _store.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); } /** Decode the tightly packed blob using this table's field layout */ @@ -191,9 +251,19 @@ library Systems { publicAccess = (_toBool(uint8(Bytes.slice1(_blob, 20)))); } + /** Tightly pack static data using this table's schema */ + function encodeStatic(address system, bool publicAccess) internal pure returns (bytes memory) { + return abi.encodePacked(system, publicAccess); + } + /** Tightly pack full data using this table's field layout */ function encode(address system, bool publicAccess) internal pure returns (bytes memory) { - return abi.encodePacked(system, publicAccess); + bytes memory _staticData = encodeStatic(system, publicAccess); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; + + return abi.encodePacked(_staticData, _encodedLengths, _dynamicData); } /** Encode keys as a bytes32 array using this table's field layout */ @@ -209,7 +279,15 @@ library Systems { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = resourceSelector; - StoreSwitch.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + StoreSwitch.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /* Delete all data for given keys */ + function _deleteRecord(bytes32 resourceSelector) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = resourceSelector; + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); } /* Delete all data for given keys (using the specified store) */ @@ -217,7 +295,7 @@ library Systems { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = resourceSelector; - _store.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + _store.deleteRecord(_tableId, _keyTuple, _fieldLayout); } } diff --git a/packages/world/src/modules/keysintable/KeysInTableHook.sol b/packages/world/src/modules/keysintable/KeysInTableHook.sol index 03622ade95..4a9c518988 100644 --- a/packages/world/src/modules/keysintable/KeysInTableHook.sol +++ b/packages/world/src/modules/keysintable/KeysInTableHook.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; +import { PackedCounter } from "@latticexyz/store/src/PackedCounter.sol"; import { FieldLayout } from "@latticexyz/store/src/FieldLayout.sol"; import { StoreHook } from "@latticexyz/store/src/StoreHook.sol"; @@ -40,11 +41,25 @@ contract KeysInTableHook is StoreHook { } } - function onBeforeSetRecord(bytes32 tableId, bytes32[] memory keyTuple, bytes memory, FieldLayout) public { + function onBeforeSetRecord( + bytes32 tableId, + bytes32[] memory keyTuple, + bytes memory, + PackedCounter, + bytes memory, + FieldLayout + ) public { handleSet(tableId, keyTuple); } - function onAfterSetRecord(bytes32 tableId, bytes32[] memory keyTuple, bytes memory, FieldLayout) public { + function onAfterSetRecord( + bytes32 tableId, + bytes32[] memory keyTuple, + bytes memory, + PackedCounter, + bytes memory, + FieldLayout + ) public { // NOOP } diff --git a/packages/world/src/modules/keysintable/query.sol b/packages/world/src/modules/keysintable/query.sol index 90946bc2c0..53769a113e 100644 --- a/packages/world/src/modules/keysintable/query.sol +++ b/packages/world/src/modules/keysintable/query.sol @@ -2,6 +2,7 @@ pragma solidity >=0.8.0; import { IStore } from "@latticexyz/store/src/IStore.sol"; +import { PackedCounter } from "@latticexyz/store/src/PackedCounter.sol"; import { getKeysInTable } from "./getKeysInTable.sol"; import { getKeysWithValue } from "../keyswithvalue/getKeysWithValue.sol"; @@ -45,7 +46,13 @@ function passesQueryFragment(bytes32[] memory keyTuple, QueryFragment memory fra if (fragment.queryType == QueryType.HasValue) { // Key must have the given value - return ArrayLib.includes(valuesToTuples(getKeysWithValue(fragment.tableId, fragment.value)), keyTuple); + return + ArrayLib.includes( + valuesToTuples( + getKeysWithValue(fragment.tableId, fragment.value, PackedCounter.wrap(bytes32(0)), new bytes(0)) + ), + keyTuple + ); } if (fragment.queryType == QueryType.Not) { @@ -55,7 +62,13 @@ function passesQueryFragment(bytes32[] memory keyTuple, QueryFragment memory fra if (fragment.queryType == QueryType.NotValue) { // Key must not have the given value - return !ArrayLib.includes(valuesToTuples(getKeysWithValue(fragment.tableId, fragment.value)), keyTuple); + return + !ArrayLib.includes( + valuesToTuples( + getKeysWithValue(fragment.tableId, fragment.value, PackedCounter.wrap(bytes32(0)), new bytes(0)) + ), + keyTuple + ); } return false; @@ -78,7 +91,13 @@ function passesQueryFragment( if (fragment.queryType == QueryType.HasValue) { // Key must be have the given value - return ArrayLib.includes(valuesToTuples(getKeysWithValue(store, fragment.tableId, fragment.value)), keyTuple); + return + ArrayLib.includes( + valuesToTuples( + getKeysWithValue(store, fragment.tableId, fragment.value, PackedCounter.wrap(bytes32(0)), new bytes(0)) + ), + keyTuple + ); } if (fragment.queryType == QueryType.Not) { @@ -88,7 +107,13 @@ function passesQueryFragment( if (fragment.queryType == QueryType.NotValue) { // Key must not have the given value - return !ArrayLib.includes(valuesToTuples(getKeysWithValue(store, fragment.tableId, fragment.value)), keyTuple); + return + !ArrayLib.includes( + valuesToTuples( + getKeysWithValue(store, fragment.tableId, fragment.value, PackedCounter.wrap(bytes32(0)), new bytes(0)) + ), + keyTuple + ); } return false; @@ -108,7 +133,9 @@ function query(QueryFragment[] memory fragments) view returns (bytes32[][] memor // Create the first interim result keyTuples = fragments[0].queryType == QueryType.Has ? getKeysInTable(fragments[0].tableId) - : valuesToTuples(getKeysWithValue(fragments[0].tableId, fragments[0].value)); + : valuesToTuples( + getKeysWithValue(fragments[0].tableId, fragments[0].value, PackedCounter.wrap(bytes32(0)), new bytes(0)) + ); for (uint256 i = 1; i < fragments.length; i++) { bytes32[][] memory result = new bytes32[][](0); @@ -143,7 +170,9 @@ function query(IStore store, QueryFragment[] memory fragments) view returns (byt // Create the first interim result keyTuples = fragments[0].queryType == QueryType.Has ? getKeysInTable(store, fragments[0].tableId) - : valuesToTuples(getKeysWithValue(store, fragments[0].tableId, fragments[0].value)); + : valuesToTuples( + getKeysWithValue(store, fragments[0].tableId, fragments[0].value, PackedCounter.wrap(bytes32(0)), new bytes(0)) + ); for (uint256 i = 1; i < fragments.length; i++) { bytes32[][] memory result = new bytes32[][](0); diff --git a/packages/world/src/modules/keysintable/tables/KeysInTable.sol b/packages/world/src/modules/keysintable/tables/KeysInTable.sol index fe4a0ef4f6..5ba1921b92 100644 --- a/packages/world/src/modules/keysintable/tables/KeysInTable.sol +++ b/packages/world/src/modules/keysintable/tables/KeysInTable.sol @@ -21,6 +21,10 @@ import { PackedCounter, PackedCounterLib } from "@latticexyz/store/src/PackedCou bytes32 constant _tableId = bytes32(abi.encodePacked(bytes16(""), bytes16("KeysInTable"))); bytes32 constant KeysInTableTableId = _tableId; +FieldLayout constant _fieldLayout = FieldLayout.wrap( + 0x0000000500000000000000000000000000000000000000000000000000000000 +); + struct KeysInTableData { bytes32[] keys0; bytes32[] keys1; @@ -32,9 +36,7 @@ struct KeysInTableData { library KeysInTable { /** Get the table values' field layout */ function getFieldLayout() internal pure returns (FieldLayout) { - uint256[] memory _fieldLayout = new uint256[](0); - - return FieldLayoutLib.encode(_fieldLayout, 5); + return _fieldLayout; } /** Get the table's key schema */ @@ -75,19 +77,17 @@ library KeysInTable { /** Register the table with its config */ function register() internal { - StoreSwitch.registerTable( - _tableId, - getFieldLayout(), - getKeySchema(), - getValueSchema(), - getKeyNames(), - getFieldNames() - ); + StoreSwitch.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + } + + /** Register the table with its config */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Register the table with its config (using the specified store) */ function register(IStore _store) internal { - _store.registerTable(_tableId, getFieldLayout(), getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + _store.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Get keys0 */ @@ -95,7 +95,16 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 0, getFieldLayout()); + bytes memory _blob = StoreSwitch.getDynamicField(_tableId, _keyTuple, 0); + return (SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_bytes32()); + } + + /** Get keys0 */ + function _getKeys0(bytes32 sourceTable) internal view returns (bytes32[] memory keys0) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = sourceTable; + + bytes memory _blob = StoreCore.getDynamicField(_tableId, _keyTuple, 0); return (SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_bytes32()); } @@ -104,7 +113,7 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - bytes memory _blob = _store.getField(_tableId, _keyTuple, 0, getFieldLayout()); + bytes memory _blob = _store.getDynamicField(_tableId, _keyTuple, 0); return (SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_bytes32()); } @@ -113,7 +122,15 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - StoreSwitch.setField(_tableId, _keyTuple, 0, EncodeArray.encode((keys0)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 0, EncodeArray.encode((keys0)), _fieldLayout); + } + + /** Set keys0 */ + function _setKeys0(bytes32 sourceTable, bytes32[] memory keys0) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = sourceTable; + + StoreCore.setField(_tableId, _keyTuple, 0, EncodeArray.encode((keys0)), _fieldLayout); } /** Set keys0 (using the specified store) */ @@ -121,7 +138,7 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - _store.setField(_tableId, _keyTuple, 0, EncodeArray.encode((keys0)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 0, EncodeArray.encode((keys0)), _fieldLayout); } /** Get the length of keys0 */ @@ -129,7 +146,18 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 0, getFieldLayout()); + uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 0, _fieldLayout); + unchecked { + return _byteLength / 32; + } + } + + /** Get the length of keys0 */ + function _lengthKeys0(bytes32 sourceTable) internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = sourceTable; + + uint256 _byteLength = StoreCore.getFieldLength(_tableId, _keyTuple, 0, _fieldLayout); unchecked { return _byteLength / 32; } @@ -140,7 +168,7 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 0, getFieldLayout()); + uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 0, _fieldLayout); unchecked { return _byteLength / 32; } @@ -159,32 +187,46 @@ library KeysInTable { _tableId, _keyTuple, 0, - getFieldLayout(), + _fieldLayout, _index * 32, (_index + 1) * 32 ); - return (Bytes.slice32(_blob, 0)); + return (bytes32(_blob)); } } /** - * Get an item of keys0 (using the specified store) + * Get an item of keys0 * (unchecked, returns invalid data if index overflows) */ - function getItemKeys0(IStore _store, bytes32 sourceTable, uint256 _index) internal view returns (bytes32) { + function _getItemKeys0(bytes32 sourceTable, uint256 _index) internal view returns (bytes32) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; unchecked { - bytes memory _blob = _store.getFieldSlice( + bytes memory _blob = StoreCore.getFieldSlice( _tableId, _keyTuple, 0, - getFieldLayout(), + _fieldLayout, _index * 32, (_index + 1) * 32 ); - return (Bytes.slice32(_blob, 0)); + return (bytes32(_blob)); + } + } + + /** + * Get an item of keys0 (using the specified store) + * (unchecked, returns invalid data if index overflows) + */ + function getItemKeys0(IStore _store, bytes32 sourceTable, uint256 _index) internal view returns (bytes32) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = sourceTable; + + unchecked { + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 0, _fieldLayout, _index * 32, (_index + 1) * 32); + return (bytes32(_blob)); } } @@ -193,7 +235,15 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - StoreSwitch.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), getFieldLayout()); + StoreSwitch.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), _fieldLayout); + } + + /** Push an element to keys0 */ + function _pushKeys0(bytes32 sourceTable, bytes32 _element) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = sourceTable; + + StoreCore.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), _fieldLayout); } /** Push an element to keys0 (using the specified store) */ @@ -201,7 +251,7 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - _store.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), getFieldLayout()); + _store.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), _fieldLayout); } /** Pop an element from keys0 */ @@ -209,7 +259,15 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - StoreSwitch.popFromField(_tableId, _keyTuple, 0, 32, getFieldLayout()); + StoreSwitch.popFromField(_tableId, _keyTuple, 0, 32, _fieldLayout); + } + + /** Pop an element from keys0 */ + function _popKeys0(bytes32 sourceTable) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = sourceTable; + + StoreCore.popFromField(_tableId, _keyTuple, 0, 32, _fieldLayout); } /** Pop an element from keys0 (using the specified store) */ @@ -217,7 +275,7 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - _store.popFromField(_tableId, _keyTuple, 0, 32, getFieldLayout()); + _store.popFromField(_tableId, _keyTuple, 0, 32, _fieldLayout); } /** @@ -229,7 +287,20 @@ library KeysInTable { _keyTuple[0] = sourceTable; unchecked { - StoreSwitch.updateInField(_tableId, _keyTuple, 0, _index * 32, abi.encodePacked((_element)), getFieldLayout()); + StoreSwitch.updateInField(_tableId, _keyTuple, 0, _index * 32, abi.encodePacked((_element)), _fieldLayout); + } + } + + /** + * Update an element of keys0 at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ + function _updateKeys0(bytes32 sourceTable, uint256 _index, bytes32 _element) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = sourceTable; + + unchecked { + StoreCore.updateInField(_tableId, _keyTuple, 0, _index * 32, abi.encodePacked((_element)), _fieldLayout); } } @@ -242,7 +313,7 @@ library KeysInTable { _keyTuple[0] = sourceTable; unchecked { - _store.updateInField(_tableId, _keyTuple, 0, _index * 32, abi.encodePacked((_element)), getFieldLayout()); + _store.updateInField(_tableId, _keyTuple, 0, _index * 32, abi.encodePacked((_element)), _fieldLayout); } } @@ -251,7 +322,16 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 1, getFieldLayout()); + bytes memory _blob = StoreSwitch.getDynamicField(_tableId, _keyTuple, 1); + return (SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_bytes32()); + } + + /** Get keys1 */ + function _getKeys1(bytes32 sourceTable) internal view returns (bytes32[] memory keys1) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = sourceTable; + + bytes memory _blob = StoreCore.getDynamicField(_tableId, _keyTuple, 1); return (SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_bytes32()); } @@ -260,7 +340,7 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - bytes memory _blob = _store.getField(_tableId, _keyTuple, 1, getFieldLayout()); + bytes memory _blob = _store.getDynamicField(_tableId, _keyTuple, 1); return (SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_bytes32()); } @@ -269,7 +349,15 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - StoreSwitch.setField(_tableId, _keyTuple, 1, EncodeArray.encode((keys1)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 1, EncodeArray.encode((keys1)), _fieldLayout); + } + + /** Set keys1 */ + function _setKeys1(bytes32 sourceTable, bytes32[] memory keys1) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = sourceTable; + + StoreCore.setField(_tableId, _keyTuple, 1, EncodeArray.encode((keys1)), _fieldLayout); } /** Set keys1 (using the specified store) */ @@ -277,7 +365,7 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - _store.setField(_tableId, _keyTuple, 1, EncodeArray.encode((keys1)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 1, EncodeArray.encode((keys1)), _fieldLayout); } /** Get the length of keys1 */ @@ -285,7 +373,18 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 1, getFieldLayout()); + uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 1, _fieldLayout); + unchecked { + return _byteLength / 32; + } + } + + /** Get the length of keys1 */ + function _lengthKeys1(bytes32 sourceTable) internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = sourceTable; + + uint256 _byteLength = StoreCore.getFieldLength(_tableId, _keyTuple, 1, _fieldLayout); unchecked { return _byteLength / 32; } @@ -296,7 +395,7 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 1, getFieldLayout()); + uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 1, _fieldLayout); unchecked { return _byteLength / 32; } @@ -315,32 +414,46 @@ library KeysInTable { _tableId, _keyTuple, 1, - getFieldLayout(), + _fieldLayout, _index * 32, (_index + 1) * 32 ); - return (Bytes.slice32(_blob, 0)); + return (bytes32(_blob)); } } /** - * Get an item of keys1 (using the specified store) + * Get an item of keys1 * (unchecked, returns invalid data if index overflows) */ - function getItemKeys1(IStore _store, bytes32 sourceTable, uint256 _index) internal view returns (bytes32) { + function _getItemKeys1(bytes32 sourceTable, uint256 _index) internal view returns (bytes32) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; unchecked { - bytes memory _blob = _store.getFieldSlice( + bytes memory _blob = StoreCore.getFieldSlice( _tableId, _keyTuple, 1, - getFieldLayout(), + _fieldLayout, _index * 32, (_index + 1) * 32 ); - return (Bytes.slice32(_blob, 0)); + return (bytes32(_blob)); + } + } + + /** + * Get an item of keys1 (using the specified store) + * (unchecked, returns invalid data if index overflows) + */ + function getItemKeys1(IStore _store, bytes32 sourceTable, uint256 _index) internal view returns (bytes32) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = sourceTable; + + unchecked { + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 1, _fieldLayout, _index * 32, (_index + 1) * 32); + return (bytes32(_blob)); } } @@ -349,7 +462,15 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - StoreSwitch.pushToField(_tableId, _keyTuple, 1, abi.encodePacked((_element)), getFieldLayout()); + StoreSwitch.pushToField(_tableId, _keyTuple, 1, abi.encodePacked((_element)), _fieldLayout); + } + + /** Push an element to keys1 */ + function _pushKeys1(bytes32 sourceTable, bytes32 _element) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = sourceTable; + + StoreCore.pushToField(_tableId, _keyTuple, 1, abi.encodePacked((_element)), _fieldLayout); } /** Push an element to keys1 (using the specified store) */ @@ -357,7 +478,7 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - _store.pushToField(_tableId, _keyTuple, 1, abi.encodePacked((_element)), getFieldLayout()); + _store.pushToField(_tableId, _keyTuple, 1, abi.encodePacked((_element)), _fieldLayout); } /** Pop an element from keys1 */ @@ -365,7 +486,15 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - StoreSwitch.popFromField(_tableId, _keyTuple, 1, 32, getFieldLayout()); + StoreSwitch.popFromField(_tableId, _keyTuple, 1, 32, _fieldLayout); + } + + /** Pop an element from keys1 */ + function _popKeys1(bytes32 sourceTable) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = sourceTable; + + StoreCore.popFromField(_tableId, _keyTuple, 1, 32, _fieldLayout); } /** Pop an element from keys1 (using the specified store) */ @@ -373,7 +502,7 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - _store.popFromField(_tableId, _keyTuple, 1, 32, getFieldLayout()); + _store.popFromField(_tableId, _keyTuple, 1, 32, _fieldLayout); } /** @@ -385,7 +514,20 @@ library KeysInTable { _keyTuple[0] = sourceTable; unchecked { - StoreSwitch.updateInField(_tableId, _keyTuple, 1, _index * 32, abi.encodePacked((_element)), getFieldLayout()); + StoreSwitch.updateInField(_tableId, _keyTuple, 1, _index * 32, abi.encodePacked((_element)), _fieldLayout); + } + } + + /** + * Update an element of keys1 at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ + function _updateKeys1(bytes32 sourceTable, uint256 _index, bytes32 _element) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = sourceTable; + + unchecked { + StoreCore.updateInField(_tableId, _keyTuple, 1, _index * 32, abi.encodePacked((_element)), _fieldLayout); } } @@ -398,7 +540,7 @@ library KeysInTable { _keyTuple[0] = sourceTable; unchecked { - _store.updateInField(_tableId, _keyTuple, 1, _index * 32, abi.encodePacked((_element)), getFieldLayout()); + _store.updateInField(_tableId, _keyTuple, 1, _index * 32, abi.encodePacked((_element)), _fieldLayout); } } @@ -407,7 +549,16 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 2, getFieldLayout()); + bytes memory _blob = StoreSwitch.getDynamicField(_tableId, _keyTuple, 2); + return (SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_bytes32()); + } + + /** Get keys2 */ + function _getKeys2(bytes32 sourceTable) internal view returns (bytes32[] memory keys2) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = sourceTable; + + bytes memory _blob = StoreCore.getDynamicField(_tableId, _keyTuple, 2); return (SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_bytes32()); } @@ -416,7 +567,7 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - bytes memory _blob = _store.getField(_tableId, _keyTuple, 2, getFieldLayout()); + bytes memory _blob = _store.getDynamicField(_tableId, _keyTuple, 2); return (SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_bytes32()); } @@ -425,7 +576,15 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - StoreSwitch.setField(_tableId, _keyTuple, 2, EncodeArray.encode((keys2)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 2, EncodeArray.encode((keys2)), _fieldLayout); + } + + /** Set keys2 */ + function _setKeys2(bytes32 sourceTable, bytes32[] memory keys2) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = sourceTable; + + StoreCore.setField(_tableId, _keyTuple, 2, EncodeArray.encode((keys2)), _fieldLayout); } /** Set keys2 (using the specified store) */ @@ -433,7 +592,7 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - _store.setField(_tableId, _keyTuple, 2, EncodeArray.encode((keys2)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 2, EncodeArray.encode((keys2)), _fieldLayout); } /** Get the length of keys2 */ @@ -441,7 +600,18 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 2, getFieldLayout()); + uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 2, _fieldLayout); + unchecked { + return _byteLength / 32; + } + } + + /** Get the length of keys2 */ + function _lengthKeys2(bytes32 sourceTable) internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = sourceTable; + + uint256 _byteLength = StoreCore.getFieldLength(_tableId, _keyTuple, 2, _fieldLayout); unchecked { return _byteLength / 32; } @@ -452,7 +622,7 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 2, getFieldLayout()); + uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 2, _fieldLayout); unchecked { return _byteLength / 32; } @@ -471,32 +641,46 @@ library KeysInTable { _tableId, _keyTuple, 2, - getFieldLayout(), + _fieldLayout, _index * 32, (_index + 1) * 32 ); - return (Bytes.slice32(_blob, 0)); + return (bytes32(_blob)); } } /** - * Get an item of keys2 (using the specified store) + * Get an item of keys2 * (unchecked, returns invalid data if index overflows) */ - function getItemKeys2(IStore _store, bytes32 sourceTable, uint256 _index) internal view returns (bytes32) { + function _getItemKeys2(bytes32 sourceTable, uint256 _index) internal view returns (bytes32) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; unchecked { - bytes memory _blob = _store.getFieldSlice( + bytes memory _blob = StoreCore.getFieldSlice( _tableId, _keyTuple, 2, - getFieldLayout(), + _fieldLayout, _index * 32, (_index + 1) * 32 ); - return (Bytes.slice32(_blob, 0)); + return (bytes32(_blob)); + } + } + + /** + * Get an item of keys2 (using the specified store) + * (unchecked, returns invalid data if index overflows) + */ + function getItemKeys2(IStore _store, bytes32 sourceTable, uint256 _index) internal view returns (bytes32) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = sourceTable; + + unchecked { + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 2, _fieldLayout, _index * 32, (_index + 1) * 32); + return (bytes32(_blob)); } } @@ -505,7 +689,15 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - StoreSwitch.pushToField(_tableId, _keyTuple, 2, abi.encodePacked((_element)), getFieldLayout()); + StoreSwitch.pushToField(_tableId, _keyTuple, 2, abi.encodePacked((_element)), _fieldLayout); + } + + /** Push an element to keys2 */ + function _pushKeys2(bytes32 sourceTable, bytes32 _element) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = sourceTable; + + StoreCore.pushToField(_tableId, _keyTuple, 2, abi.encodePacked((_element)), _fieldLayout); } /** Push an element to keys2 (using the specified store) */ @@ -513,7 +705,7 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - _store.pushToField(_tableId, _keyTuple, 2, abi.encodePacked((_element)), getFieldLayout()); + _store.pushToField(_tableId, _keyTuple, 2, abi.encodePacked((_element)), _fieldLayout); } /** Pop an element from keys2 */ @@ -521,7 +713,15 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - StoreSwitch.popFromField(_tableId, _keyTuple, 2, 32, getFieldLayout()); + StoreSwitch.popFromField(_tableId, _keyTuple, 2, 32, _fieldLayout); + } + + /** Pop an element from keys2 */ + function _popKeys2(bytes32 sourceTable) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = sourceTable; + + StoreCore.popFromField(_tableId, _keyTuple, 2, 32, _fieldLayout); } /** Pop an element from keys2 (using the specified store) */ @@ -529,7 +729,7 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - _store.popFromField(_tableId, _keyTuple, 2, 32, getFieldLayout()); + _store.popFromField(_tableId, _keyTuple, 2, 32, _fieldLayout); } /** @@ -541,7 +741,20 @@ library KeysInTable { _keyTuple[0] = sourceTable; unchecked { - StoreSwitch.updateInField(_tableId, _keyTuple, 2, _index * 32, abi.encodePacked((_element)), getFieldLayout()); + StoreSwitch.updateInField(_tableId, _keyTuple, 2, _index * 32, abi.encodePacked((_element)), _fieldLayout); + } + } + + /** + * Update an element of keys2 at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ + function _updateKeys2(bytes32 sourceTable, uint256 _index, bytes32 _element) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = sourceTable; + + unchecked { + StoreCore.updateInField(_tableId, _keyTuple, 2, _index * 32, abi.encodePacked((_element)), _fieldLayout); } } @@ -554,7 +767,7 @@ library KeysInTable { _keyTuple[0] = sourceTable; unchecked { - _store.updateInField(_tableId, _keyTuple, 2, _index * 32, abi.encodePacked((_element)), getFieldLayout()); + _store.updateInField(_tableId, _keyTuple, 2, _index * 32, abi.encodePacked((_element)), _fieldLayout); } } @@ -563,7 +776,16 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 3, getFieldLayout()); + bytes memory _blob = StoreSwitch.getDynamicField(_tableId, _keyTuple, 3); + return (SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_bytes32()); + } + + /** Get keys3 */ + function _getKeys3(bytes32 sourceTable) internal view returns (bytes32[] memory keys3) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = sourceTable; + + bytes memory _blob = StoreCore.getDynamicField(_tableId, _keyTuple, 3); return (SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_bytes32()); } @@ -572,7 +794,7 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - bytes memory _blob = _store.getField(_tableId, _keyTuple, 3, getFieldLayout()); + bytes memory _blob = _store.getDynamicField(_tableId, _keyTuple, 3); return (SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_bytes32()); } @@ -581,7 +803,15 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - StoreSwitch.setField(_tableId, _keyTuple, 3, EncodeArray.encode((keys3)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 3, EncodeArray.encode((keys3)), _fieldLayout); + } + + /** Set keys3 */ + function _setKeys3(bytes32 sourceTable, bytes32[] memory keys3) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = sourceTable; + + StoreCore.setField(_tableId, _keyTuple, 3, EncodeArray.encode((keys3)), _fieldLayout); } /** Set keys3 (using the specified store) */ @@ -589,7 +819,7 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - _store.setField(_tableId, _keyTuple, 3, EncodeArray.encode((keys3)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 3, EncodeArray.encode((keys3)), _fieldLayout); } /** Get the length of keys3 */ @@ -597,7 +827,18 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 3, getFieldLayout()); + uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 3, _fieldLayout); + unchecked { + return _byteLength / 32; + } + } + + /** Get the length of keys3 */ + function _lengthKeys3(bytes32 sourceTable) internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = sourceTable; + + uint256 _byteLength = StoreCore.getFieldLength(_tableId, _keyTuple, 3, _fieldLayout); unchecked { return _byteLength / 32; } @@ -608,7 +849,7 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 3, getFieldLayout()); + uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 3, _fieldLayout); unchecked { return _byteLength / 32; } @@ -627,32 +868,46 @@ library KeysInTable { _tableId, _keyTuple, 3, - getFieldLayout(), + _fieldLayout, _index * 32, (_index + 1) * 32 ); - return (Bytes.slice32(_blob, 0)); + return (bytes32(_blob)); } } /** - * Get an item of keys3 (using the specified store) + * Get an item of keys3 * (unchecked, returns invalid data if index overflows) */ - function getItemKeys3(IStore _store, bytes32 sourceTable, uint256 _index) internal view returns (bytes32) { + function _getItemKeys3(bytes32 sourceTable, uint256 _index) internal view returns (bytes32) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; unchecked { - bytes memory _blob = _store.getFieldSlice( + bytes memory _blob = StoreCore.getFieldSlice( _tableId, _keyTuple, 3, - getFieldLayout(), + _fieldLayout, _index * 32, (_index + 1) * 32 ); - return (Bytes.slice32(_blob, 0)); + return (bytes32(_blob)); + } + } + + /** + * Get an item of keys3 (using the specified store) + * (unchecked, returns invalid data if index overflows) + */ + function getItemKeys3(IStore _store, bytes32 sourceTable, uint256 _index) internal view returns (bytes32) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = sourceTable; + + unchecked { + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 3, _fieldLayout, _index * 32, (_index + 1) * 32); + return (bytes32(_blob)); } } @@ -661,7 +916,15 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - StoreSwitch.pushToField(_tableId, _keyTuple, 3, abi.encodePacked((_element)), getFieldLayout()); + StoreSwitch.pushToField(_tableId, _keyTuple, 3, abi.encodePacked((_element)), _fieldLayout); + } + + /** Push an element to keys3 */ + function _pushKeys3(bytes32 sourceTable, bytes32 _element) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = sourceTable; + + StoreCore.pushToField(_tableId, _keyTuple, 3, abi.encodePacked((_element)), _fieldLayout); } /** Push an element to keys3 (using the specified store) */ @@ -669,7 +932,7 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - _store.pushToField(_tableId, _keyTuple, 3, abi.encodePacked((_element)), getFieldLayout()); + _store.pushToField(_tableId, _keyTuple, 3, abi.encodePacked((_element)), _fieldLayout); } /** Pop an element from keys3 */ @@ -677,7 +940,15 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - StoreSwitch.popFromField(_tableId, _keyTuple, 3, 32, getFieldLayout()); + StoreSwitch.popFromField(_tableId, _keyTuple, 3, 32, _fieldLayout); + } + + /** Pop an element from keys3 */ + function _popKeys3(bytes32 sourceTable) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = sourceTable; + + StoreCore.popFromField(_tableId, _keyTuple, 3, 32, _fieldLayout); } /** Pop an element from keys3 (using the specified store) */ @@ -685,7 +956,7 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - _store.popFromField(_tableId, _keyTuple, 3, 32, getFieldLayout()); + _store.popFromField(_tableId, _keyTuple, 3, 32, _fieldLayout); } /** @@ -697,7 +968,20 @@ library KeysInTable { _keyTuple[0] = sourceTable; unchecked { - StoreSwitch.updateInField(_tableId, _keyTuple, 3, _index * 32, abi.encodePacked((_element)), getFieldLayout()); + StoreSwitch.updateInField(_tableId, _keyTuple, 3, _index * 32, abi.encodePacked((_element)), _fieldLayout); + } + } + + /** + * Update an element of keys3 at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ + function _updateKeys3(bytes32 sourceTable, uint256 _index, bytes32 _element) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = sourceTable; + + unchecked { + StoreCore.updateInField(_tableId, _keyTuple, 3, _index * 32, abi.encodePacked((_element)), _fieldLayout); } } @@ -710,7 +994,7 @@ library KeysInTable { _keyTuple[0] = sourceTable; unchecked { - _store.updateInField(_tableId, _keyTuple, 3, _index * 32, abi.encodePacked((_element)), getFieldLayout()); + _store.updateInField(_tableId, _keyTuple, 3, _index * 32, abi.encodePacked((_element)), _fieldLayout); } } @@ -719,7 +1003,16 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 4, getFieldLayout()); + bytes memory _blob = StoreSwitch.getDynamicField(_tableId, _keyTuple, 4); + return (SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_bytes32()); + } + + /** Get keys4 */ + function _getKeys4(bytes32 sourceTable) internal view returns (bytes32[] memory keys4) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = sourceTable; + + bytes memory _blob = StoreCore.getDynamicField(_tableId, _keyTuple, 4); return (SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_bytes32()); } @@ -728,7 +1021,7 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - bytes memory _blob = _store.getField(_tableId, _keyTuple, 4, getFieldLayout()); + bytes memory _blob = _store.getDynamicField(_tableId, _keyTuple, 4); return (SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_bytes32()); } @@ -737,7 +1030,15 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - StoreSwitch.setField(_tableId, _keyTuple, 4, EncodeArray.encode((keys4)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 4, EncodeArray.encode((keys4)), _fieldLayout); + } + + /** Set keys4 */ + function _setKeys4(bytes32 sourceTable, bytes32[] memory keys4) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = sourceTable; + + StoreCore.setField(_tableId, _keyTuple, 4, EncodeArray.encode((keys4)), _fieldLayout); } /** Set keys4 (using the specified store) */ @@ -745,7 +1046,7 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - _store.setField(_tableId, _keyTuple, 4, EncodeArray.encode((keys4)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 4, EncodeArray.encode((keys4)), _fieldLayout); } /** Get the length of keys4 */ @@ -753,7 +1054,18 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 4, getFieldLayout()); + uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 4, _fieldLayout); + unchecked { + return _byteLength / 32; + } + } + + /** Get the length of keys4 */ + function _lengthKeys4(bytes32 sourceTable) internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = sourceTable; + + uint256 _byteLength = StoreCore.getFieldLength(_tableId, _keyTuple, 4, _fieldLayout); unchecked { return _byteLength / 32; } @@ -764,7 +1076,7 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 4, getFieldLayout()); + uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 4, _fieldLayout); unchecked { return _byteLength / 32; } @@ -783,32 +1095,46 @@ library KeysInTable { _tableId, _keyTuple, 4, - getFieldLayout(), + _fieldLayout, _index * 32, (_index + 1) * 32 ); - return (Bytes.slice32(_blob, 0)); + return (bytes32(_blob)); } } /** - * Get an item of keys4 (using the specified store) + * Get an item of keys4 * (unchecked, returns invalid data if index overflows) */ - function getItemKeys4(IStore _store, bytes32 sourceTable, uint256 _index) internal view returns (bytes32) { + function _getItemKeys4(bytes32 sourceTable, uint256 _index) internal view returns (bytes32) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; unchecked { - bytes memory _blob = _store.getFieldSlice( + bytes memory _blob = StoreCore.getFieldSlice( _tableId, _keyTuple, 4, - getFieldLayout(), + _fieldLayout, _index * 32, (_index + 1) * 32 ); - return (Bytes.slice32(_blob, 0)); + return (bytes32(_blob)); + } + } + + /** + * Get an item of keys4 (using the specified store) + * (unchecked, returns invalid data if index overflows) + */ + function getItemKeys4(IStore _store, bytes32 sourceTable, uint256 _index) internal view returns (bytes32) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = sourceTable; + + unchecked { + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 4, _fieldLayout, _index * 32, (_index + 1) * 32); + return (bytes32(_blob)); } } @@ -817,7 +1143,15 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - StoreSwitch.pushToField(_tableId, _keyTuple, 4, abi.encodePacked((_element)), getFieldLayout()); + StoreSwitch.pushToField(_tableId, _keyTuple, 4, abi.encodePacked((_element)), _fieldLayout); + } + + /** Push an element to keys4 */ + function _pushKeys4(bytes32 sourceTable, bytes32 _element) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = sourceTable; + + StoreCore.pushToField(_tableId, _keyTuple, 4, abi.encodePacked((_element)), _fieldLayout); } /** Push an element to keys4 (using the specified store) */ @@ -825,7 +1159,7 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - _store.pushToField(_tableId, _keyTuple, 4, abi.encodePacked((_element)), getFieldLayout()); + _store.pushToField(_tableId, _keyTuple, 4, abi.encodePacked((_element)), _fieldLayout); } /** Pop an element from keys4 */ @@ -833,7 +1167,15 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - StoreSwitch.popFromField(_tableId, _keyTuple, 4, 32, getFieldLayout()); + StoreSwitch.popFromField(_tableId, _keyTuple, 4, 32, _fieldLayout); + } + + /** Pop an element from keys4 */ + function _popKeys4(bytes32 sourceTable) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = sourceTable; + + StoreCore.popFromField(_tableId, _keyTuple, 4, 32, _fieldLayout); } /** Pop an element from keys4 (using the specified store) */ @@ -841,7 +1183,7 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - _store.popFromField(_tableId, _keyTuple, 4, 32, getFieldLayout()); + _store.popFromField(_tableId, _keyTuple, 4, 32, _fieldLayout); } /** @@ -853,7 +1195,20 @@ library KeysInTable { _keyTuple[0] = sourceTable; unchecked { - StoreSwitch.updateInField(_tableId, _keyTuple, 4, _index * 32, abi.encodePacked((_element)), getFieldLayout()); + StoreSwitch.updateInField(_tableId, _keyTuple, 4, _index * 32, abi.encodePacked((_element)), _fieldLayout); + } + } + + /** + * Update an element of keys4 at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ + function _updateKeys4(bytes32 sourceTable, uint256 _index, bytes32 _element) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = sourceTable; + + unchecked { + StoreCore.updateInField(_tableId, _keyTuple, 4, _index * 32, abi.encodePacked((_element)), _fieldLayout); } } @@ -866,7 +1221,7 @@ library KeysInTable { _keyTuple[0] = sourceTable; unchecked { - _store.updateInField(_tableId, _keyTuple, 4, _index * 32, abi.encodePacked((_element)), getFieldLayout()); + _store.updateInField(_tableId, _keyTuple, 4, _index * 32, abi.encodePacked((_element)), _fieldLayout); } } @@ -875,7 +1230,16 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - bytes memory _blob = StoreSwitch.getRecord(_tableId, _keyTuple, getFieldLayout()); + bytes memory _blob = StoreSwitch.getRecord(_tableId, _keyTuple, _fieldLayout); + return decode(_blob); + } + + /** Get the full data */ + function _get(bytes32 sourceTable) internal view returns (KeysInTableData memory _table) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = sourceTable; + + bytes memory _blob = StoreCore.getRecord(_tableId, _keyTuple, _fieldLayout); return decode(_blob); } @@ -884,7 +1248,7 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - bytes memory _blob = _store.getRecord(_tableId, _keyTuple, getFieldLayout()); + bytes memory _blob = _store.getRecord(_tableId, _keyTuple, _fieldLayout); return decode(_blob); } @@ -897,12 +1261,33 @@ library KeysInTable { bytes32[] memory keys3, bytes32[] memory keys4 ) internal { - bytes memory _data = encode(keys0, keys1, keys2, keys3, keys4); + bytes memory _staticData; + PackedCounter _encodedLengths = encodeLengths(keys0, keys1, keys2, keys3, keys4); + bytes memory _dynamicData = encodeDynamic(keys0, keys1, keys2, keys3, keys4); bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - StoreSwitch.setRecord(_tableId, _keyTuple, _data, getFieldLayout()); + StoreSwitch.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); + } + + /** Set the full data using individual values */ + function _set( + bytes32 sourceTable, + bytes32[] memory keys0, + bytes32[] memory keys1, + bytes32[] memory keys2, + bytes32[] memory keys3, + bytes32[] memory keys4 + ) internal { + bytes memory _staticData; + PackedCounter _encodedLengths = encodeLengths(keys0, keys1, keys2, keys3, keys4); + bytes memory _dynamicData = encodeDynamic(keys0, keys1, keys2, keys3, keys4); + + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = sourceTable; + + StoreCore.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); } /** Set the full data using individual values (using the specified store) */ @@ -915,12 +1300,14 @@ library KeysInTable { bytes32[] memory keys3, bytes32[] memory keys4 ) internal { - bytes memory _data = encode(keys0, keys1, keys2, keys3, keys4); + bytes memory _staticData; + PackedCounter _encodedLengths = encodeLengths(keys0, keys1, keys2, keys3, keys4); + bytes memory _dynamicData = encodeDynamic(keys0, keys1, keys2, keys3, keys4); bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - _store.setRecord(_tableId, _keyTuple, _data, getFieldLayout()); + _store.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); } /** Set the full data using the data struct */ @@ -928,6 +1315,11 @@ library KeysInTable { set(sourceTable, _table.keys0, _table.keys1, _table.keys2, _table.keys3, _table.keys4); } + /** Set the full data using the data struct */ + function _set(bytes32 sourceTable, KeysInTableData memory _table) internal { + set(sourceTable, _table.keys0, _table.keys1, _table.keys2, _table.keys3, _table.keys4); + } + /** Set the full data using the data struct (using the specified store) */ function set(IStore _store, bytes32 sourceTable, KeysInTableData memory _table) internal { set(_store, sourceTable, _table.keys0, _table.keys1, _table.keys2, _table.keys3, _table.keys4); @@ -977,15 +1369,14 @@ library KeysInTable { } } - /** Tightly pack full data using this table's field layout */ - function encode( + /** Tightly pack dynamic data using this table's schema */ + function encodeLengths( bytes32[] memory keys0, bytes32[] memory keys1, bytes32[] memory keys2, bytes32[] memory keys3, bytes32[] memory keys4 - ) internal pure returns (bytes memory) { - PackedCounter _encodedLengths; + ) internal pure returns (PackedCounter _encodedLengths) { // Lengths are effectively checked during copy by 2**40 bytes exceeding gas limits unchecked { _encodedLengths = PackedCounterLib.pack( @@ -996,10 +1387,18 @@ library KeysInTable { keys4.length * 32 ); } + } + /** Tightly pack dynamic data using this table's schema */ + function encodeDynamic( + bytes32[] memory keys0, + bytes32[] memory keys1, + bytes32[] memory keys2, + bytes32[] memory keys3, + bytes32[] memory keys4 + ) internal pure returns (bytes memory) { return abi.encodePacked( - _encodedLengths.unwrap(), EncodeArray.encode((keys0)), EncodeArray.encode((keys1)), EncodeArray.encode((keys2)), @@ -1008,6 +1407,21 @@ library KeysInTable { ); } + /** Tightly pack full data using this table's field layout */ + function encode( + bytes32[] memory keys0, + bytes32[] memory keys1, + bytes32[] memory keys2, + bytes32[] memory keys3, + bytes32[] memory keys4 + ) internal pure returns (bytes memory) { + bytes memory _staticData; + PackedCounter _encodedLengths = encodeLengths(keys0, keys1, keys2, keys3, keys4); + bytes memory _dynamicData = encodeDynamic(keys0, keys1, keys2, keys3, keys4); + + return abi.encodePacked(_staticData, _encodedLengths, _dynamicData); + } + /** Encode keys as a bytes32 array using this table's field layout */ function encodeKeyTuple(bytes32 sourceTable) internal pure returns (bytes32[] memory) { bytes32[] memory _keyTuple = new bytes32[](1); @@ -1021,7 +1435,15 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - StoreSwitch.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + StoreSwitch.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /* Delete all data for given keys */ + function _deleteRecord(bytes32 sourceTable) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = sourceTable; + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); } /* Delete all data for given keys (using the specified store) */ @@ -1029,6 +1451,6 @@ library KeysInTable { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - _store.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + _store.deleteRecord(_tableId, _keyTuple, _fieldLayout); } } diff --git a/packages/world/src/modules/keysintable/tables/UsedKeysIndex.sol b/packages/world/src/modules/keysintable/tables/UsedKeysIndex.sol index b29e54cab8..c50753a03b 100644 --- a/packages/world/src/modules/keysintable/tables/UsedKeysIndex.sol +++ b/packages/world/src/modules/keysintable/tables/UsedKeysIndex.sol @@ -21,14 +21,14 @@ import { PackedCounter, PackedCounterLib } from "@latticexyz/store/src/PackedCou bytes32 constant _tableId = bytes32(abi.encodePacked(bytes16(""), bytes16("UsedKeysIndex"))); bytes32 constant UsedKeysIndexTableId = _tableId; +FieldLayout constant _fieldLayout = FieldLayout.wrap( + 0x0006020001050000000000000000000000000000000000000000000000000000 +); + library UsedKeysIndex { /** Get the table values' field layout */ function getFieldLayout() internal pure returns (FieldLayout) { - uint256[] memory _fieldLayout = new uint256[](2); - _fieldLayout[0] = 1; - _fieldLayout[1] = 5; - - return FieldLayoutLib.encode(_fieldLayout, 0); + return _fieldLayout; } /** Get the table's key schema */ @@ -65,19 +65,17 @@ library UsedKeysIndex { /** Register the table with its config */ function register() internal { - StoreSwitch.registerTable( - _tableId, - getFieldLayout(), - getKeySchema(), - getValueSchema(), - getKeyNames(), - getFieldNames() - ); + StoreSwitch.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + } + + /** Register the table with its config */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Register the table with its config (using the specified store) */ function register(IStore _store) internal { - _store.registerTable(_tableId, getFieldLayout(), getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + _store.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Get has */ @@ -86,8 +84,18 @@ library UsedKeysIndex { _keyTuple[0] = sourceTable; _keyTuple[1] = keysHash; - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (_toBool(uint8(Bytes.slice1(_blob, 0)))); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (_toBool(uint8(bytes1(_blob)))); + } + + /** Get has */ + function _getHas(bytes32 sourceTable, bytes32 keysHash) internal view returns (bool has) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = sourceTable; + _keyTuple[1] = keysHash; + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (_toBool(uint8(bytes1(_blob)))); } /** Get has (using the specified store) */ @@ -96,8 +104,8 @@ library UsedKeysIndex { _keyTuple[0] = sourceTable; _keyTuple[1] = keysHash; - bytes memory _blob = _store.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (_toBool(uint8(Bytes.slice1(_blob, 0)))); + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (_toBool(uint8(bytes1(_blob)))); } /** Set has */ @@ -106,7 +114,16 @@ library UsedKeysIndex { _keyTuple[0] = sourceTable; _keyTuple[1] = keysHash; - StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((has)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((has)), _fieldLayout); + } + + /** Set has */ + function _setHas(bytes32 sourceTable, bytes32 keysHash, bool has) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = sourceTable; + _keyTuple[1] = keysHash; + + StoreCore.setField(_tableId, _keyTuple, 0, abi.encodePacked((has)), _fieldLayout); } /** Set has (using the specified store) */ @@ -115,7 +132,7 @@ library UsedKeysIndex { _keyTuple[0] = sourceTable; _keyTuple[1] = keysHash; - _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((has)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((has)), _fieldLayout); } /** Get index */ @@ -124,8 +141,18 @@ library UsedKeysIndex { _keyTuple[0] = sourceTable; _keyTuple[1] = keysHash; - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 1, getFieldLayout()); - return (uint40(Bytes.slice5(_blob, 0))); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 1, _fieldLayout); + return (uint40(bytes5(_blob))); + } + + /** Get index */ + function _getIndex(bytes32 sourceTable, bytes32 keysHash) internal view returns (uint40 index) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = sourceTable; + _keyTuple[1] = keysHash; + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 1, _fieldLayout); + return (uint40(bytes5(_blob))); } /** Get index (using the specified store) */ @@ -134,8 +161,8 @@ library UsedKeysIndex { _keyTuple[0] = sourceTable; _keyTuple[1] = keysHash; - bytes memory _blob = _store.getField(_tableId, _keyTuple, 1, getFieldLayout()); - return (uint40(Bytes.slice5(_blob, 0))); + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 1, _fieldLayout); + return (uint40(bytes5(_blob))); } /** Set index */ @@ -144,7 +171,16 @@ library UsedKeysIndex { _keyTuple[0] = sourceTable; _keyTuple[1] = keysHash; - StoreSwitch.setField(_tableId, _keyTuple, 1, abi.encodePacked((index)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 1, abi.encodePacked((index)), _fieldLayout); + } + + /** Set index */ + function _setIndex(bytes32 sourceTable, bytes32 keysHash, uint40 index) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = sourceTable; + _keyTuple[1] = keysHash; + + StoreCore.setField(_tableId, _keyTuple, 1, abi.encodePacked((index)), _fieldLayout); } /** Set index (using the specified store) */ @@ -153,7 +189,7 @@ library UsedKeysIndex { _keyTuple[0] = sourceTable; _keyTuple[1] = keysHash; - _store.setField(_tableId, _keyTuple, 1, abi.encodePacked((index)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 1, abi.encodePacked((index)), _fieldLayout); } /** Get the full data */ @@ -162,7 +198,17 @@ library UsedKeysIndex { _keyTuple[0] = sourceTable; _keyTuple[1] = keysHash; - bytes memory _blob = StoreSwitch.getRecord(_tableId, _keyTuple, getFieldLayout()); + bytes memory _blob = StoreSwitch.getRecord(_tableId, _keyTuple, _fieldLayout); + return decode(_blob); + } + + /** Get the full data */ + function _get(bytes32 sourceTable, bytes32 keysHash) internal view returns (bool has, uint40 index) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = sourceTable; + _keyTuple[1] = keysHash; + + bytes memory _blob = StoreCore.getRecord(_tableId, _keyTuple, _fieldLayout); return decode(_blob); } @@ -172,30 +218,50 @@ library UsedKeysIndex { _keyTuple[0] = sourceTable; _keyTuple[1] = keysHash; - bytes memory _blob = _store.getRecord(_tableId, _keyTuple, getFieldLayout()); + bytes memory _blob = _store.getRecord(_tableId, _keyTuple, _fieldLayout); return decode(_blob); } /** Set the full data using individual values */ function set(bytes32 sourceTable, bytes32 keysHash, bool has, uint40 index) internal { - bytes memory _data = encode(has, index); + bytes memory _staticData = encodeStatic(has, index); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; + + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = sourceTable; + _keyTuple[1] = keysHash; + + StoreSwitch.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); + } + + /** Set the full data using individual values */ + function _set(bytes32 sourceTable, bytes32 keysHash, bool has, uint40 index) internal { + bytes memory _staticData = encodeStatic(has, index); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; bytes32[] memory _keyTuple = new bytes32[](2); _keyTuple[0] = sourceTable; _keyTuple[1] = keysHash; - StoreSwitch.setRecord(_tableId, _keyTuple, _data, getFieldLayout()); + StoreCore.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); } /** Set the full data using individual values (using the specified store) */ function set(IStore _store, bytes32 sourceTable, bytes32 keysHash, bool has, uint40 index) internal { - bytes memory _data = encode(has, index); + bytes memory _staticData = encodeStatic(has, index); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; bytes32[] memory _keyTuple = new bytes32[](2); _keyTuple[0] = sourceTable; _keyTuple[1] = keysHash; - _store.setRecord(_tableId, _keyTuple, _data, getFieldLayout()); + _store.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); } /** Decode the tightly packed blob using this table's field layout */ @@ -205,9 +271,19 @@ library UsedKeysIndex { index = (uint40(Bytes.slice5(_blob, 1))); } + /** Tightly pack static data using this table's schema */ + function encodeStatic(bool has, uint40 index) internal pure returns (bytes memory) { + return abi.encodePacked(has, index); + } + /** Tightly pack full data using this table's field layout */ function encode(bool has, uint40 index) internal pure returns (bytes memory) { - return abi.encodePacked(has, index); + bytes memory _staticData = encodeStatic(has, index); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; + + return abi.encodePacked(_staticData, _encodedLengths, _dynamicData); } /** Encode keys as a bytes32 array using this table's field layout */ @@ -225,7 +301,16 @@ library UsedKeysIndex { _keyTuple[0] = sourceTable; _keyTuple[1] = keysHash; - StoreSwitch.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + StoreSwitch.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /* Delete all data for given keys */ + function _deleteRecord(bytes32 sourceTable, bytes32 keysHash) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = sourceTable; + _keyTuple[1] = keysHash; + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); } /* Delete all data for given keys (using the specified store) */ @@ -234,7 +319,7 @@ library UsedKeysIndex { _keyTuple[0] = sourceTable; _keyTuple[1] = keysHash; - _store.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + _store.deleteRecord(_tableId, _keyTuple, _fieldLayout); } } diff --git a/packages/world/src/modules/keyswithvalue/KeysWithValueHook.sol b/packages/world/src/modules/keyswithvalue/KeysWithValueHook.sol index f0326407cd..3c3c822c0f 100644 --- a/packages/world/src/modules/keyswithvalue/KeysWithValueHook.sol +++ b/packages/world/src/modules/keyswithvalue/KeysWithValueHook.sol @@ -5,6 +5,7 @@ import { StoreHook } from "@latticexyz/store/src/StoreHook.sol"; import { Bytes } from "@latticexyz/store/src/Bytes.sol"; import { FieldLayout } from "@latticexyz/store/src/FieldLayout.sol"; import { StoreSwitch } from "@latticexyz/store/src/StoreSwitch.sol"; +import { PackedCounter } from "@latticexyz/store/src/PackedCounter.sol"; import { IBaseWorld } from "../../interfaces/IBaseWorld.sol"; import { ResourceSelector } from "../../ResourceSelector.sol"; @@ -32,7 +33,9 @@ contract KeysWithValueHook is StoreHook { function onBeforeSetRecord( bytes32 sourceTableId, bytes32[] memory keyTuple, - bytes memory data, + bytes memory staticData, + PackedCounter encodedLengths, + bytes memory dynamicData, FieldLayout fieldLayout ) public { bytes32 targetTableId = getTargetTableSelector(MODULE_NAMESPACE, sourceTableId); @@ -44,13 +47,21 @@ contract KeysWithValueHook is StoreHook { _removeKeyFromList(targetTableId, keyTuple[0], previousValue); // Push the key to the list of keys with the new value + bytes memory data; + if (dynamicData.length > 0) { + data = abi.encodePacked(staticData, encodedLengths, dynamicData); + } else { + data = staticData; + } KeysWithValue.push(targetTableId, keccak256(data), keyTuple[0]); } function onAfterSetRecord( bytes32 sourceTableId, bytes32[] memory keyTuple, - bytes memory data, + bytes memory staticData, + PackedCounter encodedLengths, + bytes memory dynamicData, FieldLayout fieldLayout ) public { // NOOP diff --git a/packages/world/src/modules/keyswithvalue/getKeysWithValue.sol b/packages/world/src/modules/keyswithvalue/getKeysWithValue.sol index f5e8605c63..e47fba0d10 100644 --- a/packages/world/src/modules/keyswithvalue/getKeysWithValue.sol +++ b/packages/world/src/modules/keyswithvalue/getKeysWithValue.sol @@ -2,6 +2,7 @@ pragma solidity >=0.8.0; import { IStore } from "@latticexyz/store/src/IStore.sol"; +import { PackedCounter } from "@latticexyz/store/src/PackedCounter.sol"; import { MODULE_NAMESPACE } from "./constants.sol"; import { KeysWithValue } from "./tables/KeysWithValue.sol"; @@ -13,11 +14,22 @@ import { getTargetTableSelector } from "../utils/getTargetTableSelector.sol"; * 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 getKeysWithValue(bytes32 tableId, bytes memory value) view returns (bytes32[] memory keysWithValue) { +function getKeysWithValue( + bytes32 tableId, + bytes memory staticData, + PackedCounter encodedLengths, + bytes memory dynamicData +) view returns (bytes32[] memory keysWithValue) { // Get the corresponding reverse mapping table bytes32 keysWithValueTableId = getTargetTableSelector(MODULE_NAMESPACE, tableId); // Get the keys with the given value + bytes memory value; + if (dynamicData.length > 0) { + value = abi.encodePacked(staticData, encodedLengths, dynamicData); + } else { + value = staticData; + } keysWithValue = KeysWithValue.get(keysWithValueTableId, keccak256(value)); } @@ -27,11 +39,19 @@ function getKeysWithValue(bytes32 tableId, bytes memory value) view returns (byt function getKeysWithValue( IStore store, bytes32 tableId, - bytes memory value + bytes memory staticData, + PackedCounter encodedLengths, + bytes memory dynamicData ) view returns (bytes32[] memory keysWithValue) { // Get the corresponding reverse mapping table bytes32 keysWithValueTableId = getTargetTableSelector(MODULE_NAMESPACE, tableId); // Get the keys with the given value + bytes memory value; + if (dynamicData.length > 0) { + value = abi.encodePacked(staticData, encodedLengths, dynamicData); + } else { + value = staticData; + } keysWithValue = KeysWithValue.get(store, keysWithValueTableId, keccak256(value)); } diff --git a/packages/world/src/modules/keyswithvalue/tables/KeysWithValue.sol b/packages/world/src/modules/keyswithvalue/tables/KeysWithValue.sol index 2ae73a6568..76db05f36f 100644 --- a/packages/world/src/modules/keyswithvalue/tables/KeysWithValue.sol +++ b/packages/world/src/modules/keyswithvalue/tables/KeysWithValue.sol @@ -18,12 +18,14 @@ import { FieldLayout, FieldLayoutLib } from "@latticexyz/store/src/FieldLayout.s import { Schema, SchemaLib } from "@latticexyz/store/src/Schema.sol"; import { PackedCounter, PackedCounterLib } from "@latticexyz/store/src/PackedCounter.sol"; +FieldLayout constant _fieldLayout = FieldLayout.wrap( + 0x0000000100000000000000000000000000000000000000000000000000000000 +); + library KeysWithValue { /** Get the table values' field layout */ function getFieldLayout() internal pure returns (FieldLayout) { - uint256[] memory _fieldLayout = new uint256[](0); - - return FieldLayoutLib.encode(_fieldLayout, 1); + return _fieldLayout; } /** Get the table's key schema */ @@ -56,19 +58,17 @@ library KeysWithValue { /** Register the table with its config */ function register(bytes32 _tableId) internal { - StoreSwitch.registerTable( - _tableId, - getFieldLayout(), - getKeySchema(), - getValueSchema(), - getKeyNames(), - getFieldNames() - ); + StoreSwitch.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + } + + /** Register the table with its config */ + function _register(bytes32 _tableId) internal { + StoreCore.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Register the table with its config (using the specified store) */ function register(IStore _store, bytes32 _tableId) internal { - _store.registerTable(_tableId, getFieldLayout(), getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + _store.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Get keysWithValue */ @@ -76,7 +76,16 @@ library KeysWithValue { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = valueHash; - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 0, getFieldLayout()); + bytes memory _blob = StoreSwitch.getDynamicField(_tableId, _keyTuple, 0); + return (SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_bytes32()); + } + + /** Get keysWithValue */ + function _get(bytes32 _tableId, bytes32 valueHash) internal view returns (bytes32[] memory keysWithValue) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = valueHash; + + bytes memory _blob = StoreCore.getDynamicField(_tableId, _keyTuple, 0); return (SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_bytes32()); } @@ -89,7 +98,7 @@ library KeysWithValue { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = valueHash; - bytes memory _blob = _store.getField(_tableId, _keyTuple, 0, getFieldLayout()); + bytes memory _blob = _store.getDynamicField(_tableId, _keyTuple, 0); return (SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_bytes32()); } @@ -98,7 +107,15 @@ library KeysWithValue { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = valueHash; - StoreSwitch.setField(_tableId, _keyTuple, 0, EncodeArray.encode((keysWithValue)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 0, EncodeArray.encode((keysWithValue)), _fieldLayout); + } + + /** Set keysWithValue */ + function _set(bytes32 _tableId, bytes32 valueHash, bytes32[] memory keysWithValue) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = valueHash; + + StoreCore.setField(_tableId, _keyTuple, 0, EncodeArray.encode((keysWithValue)), _fieldLayout); } /** Set keysWithValue (using the specified store) */ @@ -106,7 +123,7 @@ library KeysWithValue { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = valueHash; - _store.setField(_tableId, _keyTuple, 0, EncodeArray.encode((keysWithValue)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 0, EncodeArray.encode((keysWithValue)), _fieldLayout); } /** Get the length of keysWithValue */ @@ -114,7 +131,18 @@ library KeysWithValue { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = valueHash; - uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 0, getFieldLayout()); + uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 0, _fieldLayout); + unchecked { + return _byteLength / 32; + } + } + + /** Get the length of keysWithValue */ + function _length(bytes32 _tableId, bytes32 valueHash) internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = valueHash; + + uint256 _byteLength = StoreCore.getFieldLength(_tableId, _keyTuple, 0, _fieldLayout); unchecked { return _byteLength / 32; } @@ -125,7 +153,7 @@ library KeysWithValue { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = valueHash; - uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 0, getFieldLayout()); + uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 0, _fieldLayout); unchecked { return _byteLength / 32; } @@ -144,32 +172,46 @@ library KeysWithValue { _tableId, _keyTuple, 0, - getFieldLayout(), + _fieldLayout, _index * 32, (_index + 1) * 32 ); - return (Bytes.slice32(_blob, 0)); + return (bytes32(_blob)); } } /** - * Get an item of keysWithValue (using the specified store) + * Get an item of keysWithValue * (unchecked, returns invalid data if index overflows) */ - function getItem(IStore _store, bytes32 _tableId, bytes32 valueHash, uint256 _index) internal view returns (bytes32) { + function _getItem(bytes32 _tableId, bytes32 valueHash, uint256 _index) internal view returns (bytes32) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = valueHash; unchecked { - bytes memory _blob = _store.getFieldSlice( + bytes memory _blob = StoreCore.getFieldSlice( _tableId, _keyTuple, 0, - getFieldLayout(), + _fieldLayout, _index * 32, (_index + 1) * 32 ); - return (Bytes.slice32(_blob, 0)); + return (bytes32(_blob)); + } + } + + /** + * Get an item of keysWithValue (using the specified store) + * (unchecked, returns invalid data if index overflows) + */ + function getItem(IStore _store, bytes32 _tableId, bytes32 valueHash, uint256 _index) internal view returns (bytes32) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = valueHash; + + unchecked { + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 0, _fieldLayout, _index * 32, (_index + 1) * 32); + return (bytes32(_blob)); } } @@ -178,7 +220,15 @@ library KeysWithValue { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = valueHash; - StoreSwitch.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), getFieldLayout()); + StoreSwitch.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), _fieldLayout); + } + + /** Push an element to keysWithValue */ + function _push(bytes32 _tableId, bytes32 valueHash, bytes32 _element) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = valueHash; + + StoreCore.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), _fieldLayout); } /** Push an element to keysWithValue (using the specified store) */ @@ -186,7 +236,7 @@ library KeysWithValue { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = valueHash; - _store.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), getFieldLayout()); + _store.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), _fieldLayout); } /** Pop an element from keysWithValue */ @@ -194,7 +244,15 @@ library KeysWithValue { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = valueHash; - StoreSwitch.popFromField(_tableId, _keyTuple, 0, 32, getFieldLayout()); + StoreSwitch.popFromField(_tableId, _keyTuple, 0, 32, _fieldLayout); + } + + /** Pop an element from keysWithValue */ + function _pop(bytes32 _tableId, bytes32 valueHash) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = valueHash; + + StoreCore.popFromField(_tableId, _keyTuple, 0, 32, _fieldLayout); } /** Pop an element from keysWithValue (using the specified store) */ @@ -202,7 +260,7 @@ library KeysWithValue { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = valueHash; - _store.popFromField(_tableId, _keyTuple, 0, 32, getFieldLayout()); + _store.popFromField(_tableId, _keyTuple, 0, 32, _fieldLayout); } /** @@ -214,7 +272,20 @@ library KeysWithValue { _keyTuple[0] = valueHash; unchecked { - StoreSwitch.updateInField(_tableId, _keyTuple, 0, _index * 32, abi.encodePacked((_element)), getFieldLayout()); + StoreSwitch.updateInField(_tableId, _keyTuple, 0, _index * 32, abi.encodePacked((_element)), _fieldLayout); + } + } + + /** + * Update an element of keysWithValue at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ + function _update(bytes32 _tableId, bytes32 valueHash, uint256 _index, bytes32 _element) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = valueHash; + + unchecked { + StoreCore.updateInField(_tableId, _keyTuple, 0, _index * 32, abi.encodePacked((_element)), _fieldLayout); } } @@ -227,19 +298,30 @@ library KeysWithValue { _keyTuple[0] = valueHash; unchecked { - _store.updateInField(_tableId, _keyTuple, 0, _index * 32, abi.encodePacked((_element)), getFieldLayout()); + _store.updateInField(_tableId, _keyTuple, 0, _index * 32, abi.encodePacked((_element)), _fieldLayout); } } - /** Tightly pack full data using this table's field layout */ - function encode(bytes32[] memory keysWithValue) internal pure returns (bytes memory) { - PackedCounter _encodedLengths; + /** Tightly pack dynamic data using this table's schema */ + function encodeLengths(bytes32[] memory keysWithValue) internal pure returns (PackedCounter _encodedLengths) { // Lengths are effectively checked during copy by 2**40 bytes exceeding gas limits unchecked { _encodedLengths = PackedCounterLib.pack(keysWithValue.length * 32); } + } - return abi.encodePacked(_encodedLengths.unwrap(), EncodeArray.encode((keysWithValue))); + /** Tightly pack dynamic data using this table's schema */ + function encodeDynamic(bytes32[] memory keysWithValue) internal pure returns (bytes memory) { + return abi.encodePacked(EncodeArray.encode((keysWithValue))); + } + + /** Tightly pack full data using this table's field layout */ + function encode(bytes32[] memory keysWithValue) internal pure returns (bytes memory) { + bytes memory _staticData; + PackedCounter _encodedLengths = encodeLengths(keysWithValue); + bytes memory _dynamicData = encodeDynamic(keysWithValue); + + return abi.encodePacked(_staticData, _encodedLengths, _dynamicData); } /** Encode keys as a bytes32 array using this table's field layout */ @@ -255,7 +337,15 @@ library KeysWithValue { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = valueHash; - StoreSwitch.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + StoreSwitch.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /* Delete all data for given keys */ + function _deleteRecord(bytes32 _tableId, bytes32 valueHash) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = valueHash; + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); } /* Delete all data for given keys (using the specified store) */ @@ -263,6 +353,6 @@ library KeysWithValue { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = valueHash; - _store.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + _store.deleteRecord(_tableId, _keyTuple, _fieldLayout); } } diff --git a/packages/world/src/modules/std-delegations/tables/CallboundDelegations.sol b/packages/world/src/modules/std-delegations/tables/CallboundDelegations.sol index a2f0f2519b..f36c9e45b5 100644 --- a/packages/world/src/modules/std-delegations/tables/CallboundDelegations.sol +++ b/packages/world/src/modules/std-delegations/tables/CallboundDelegations.sol @@ -21,13 +21,14 @@ import { PackedCounter, PackedCounterLib } from "@latticexyz/store/src/PackedCou bytes32 constant _tableId = bytes32(abi.encodePacked(bytes16(""), bytes16("CallboundDelegat"))); bytes32 constant CallboundDelegationsTableId = _tableId; +FieldLayout constant _fieldLayout = FieldLayout.wrap( + 0x0020010020000000000000000000000000000000000000000000000000000000 +); + library CallboundDelegations { /** Get the table values' field layout */ function getFieldLayout() internal pure returns (FieldLayout) { - uint256[] memory _fieldLayout = new uint256[](1); - _fieldLayout[0] = 32; - - return FieldLayoutLib.encode(_fieldLayout, 0); + return _fieldLayout; } /** Get the table's key schema */ @@ -66,19 +67,17 @@ library CallboundDelegations { /** Register the table with its config */ function register() internal { - StoreSwitch.registerTable( - _tableId, - getFieldLayout(), - getKeySchema(), - getValueSchema(), - getKeyNames(), - getFieldNames() - ); + StoreSwitch.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + } + + /** Register the table with its config */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Register the table with its config (using the specified store) */ function register(IStore _store) internal { - _store.registerTable(_tableId, getFieldLayout(), getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + _store.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Get availableCalls */ @@ -94,8 +93,25 @@ library CallboundDelegations { _keyTuple[2] = resourceSelector; _keyTuple[3] = funcSelectorAndArgsHash; - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (uint256(Bytes.slice32(_blob, 0))); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint256(bytes32(_blob))); + } + + /** Get availableCalls */ + function _get( + address delegator, + address delegatee, + bytes32 resourceSelector, + bytes32 funcSelectorAndArgsHash + ) internal view returns (uint256 availableCalls) { + bytes32[] memory _keyTuple = new bytes32[](4); + _keyTuple[0] = bytes32(uint256(uint160(delegator))); + _keyTuple[1] = bytes32(uint256(uint160(delegatee))); + _keyTuple[2] = resourceSelector; + _keyTuple[3] = funcSelectorAndArgsHash; + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint256(bytes32(_blob))); } /** Get availableCalls (using the specified store) */ @@ -112,8 +128,8 @@ library CallboundDelegations { _keyTuple[2] = resourceSelector; _keyTuple[3] = funcSelectorAndArgsHash; - bytes memory _blob = _store.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (uint256(Bytes.slice32(_blob, 0))); + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint256(bytes32(_blob))); } /** Set availableCalls */ @@ -130,7 +146,24 @@ library CallboundDelegations { _keyTuple[2] = resourceSelector; _keyTuple[3] = funcSelectorAndArgsHash; - StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((availableCalls)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((availableCalls)), _fieldLayout); + } + + /** Set availableCalls */ + function _set( + address delegator, + address delegatee, + bytes32 resourceSelector, + bytes32 funcSelectorAndArgsHash, + uint256 availableCalls + ) internal { + bytes32[] memory _keyTuple = new bytes32[](4); + _keyTuple[0] = bytes32(uint256(uint160(delegator))); + _keyTuple[1] = bytes32(uint256(uint160(delegatee))); + _keyTuple[2] = resourceSelector; + _keyTuple[3] = funcSelectorAndArgsHash; + + StoreCore.setField(_tableId, _keyTuple, 0, abi.encodePacked((availableCalls)), _fieldLayout); } /** Set availableCalls (using the specified store) */ @@ -148,12 +181,22 @@ library CallboundDelegations { _keyTuple[2] = resourceSelector; _keyTuple[3] = funcSelectorAndArgsHash; - _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((availableCalls)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((availableCalls)), _fieldLayout); + } + + /** Tightly pack static data using this table's schema */ + function encodeStatic(uint256 availableCalls) internal pure returns (bytes memory) { + return abi.encodePacked(availableCalls); } /** Tightly pack full data using this table's field layout */ function encode(uint256 availableCalls) internal pure returns (bytes memory) { - return abi.encodePacked(availableCalls); + bytes memory _staticData = encodeStatic(availableCalls); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; + + return abi.encodePacked(_staticData, _encodedLengths, _dynamicData); } /** Encode keys as a bytes32 array using this table's field layout */ @@ -185,7 +228,23 @@ library CallboundDelegations { _keyTuple[2] = resourceSelector; _keyTuple[3] = funcSelectorAndArgsHash; - StoreSwitch.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + StoreSwitch.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /* Delete all data for given keys */ + function _deleteRecord( + address delegator, + address delegatee, + bytes32 resourceSelector, + bytes32 funcSelectorAndArgsHash + ) internal { + bytes32[] memory _keyTuple = new bytes32[](4); + _keyTuple[0] = bytes32(uint256(uint160(delegator))); + _keyTuple[1] = bytes32(uint256(uint160(delegatee))); + _keyTuple[2] = resourceSelector; + _keyTuple[3] = funcSelectorAndArgsHash; + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); } /* Delete all data for given keys (using the specified store) */ @@ -202,6 +261,6 @@ library CallboundDelegations { _keyTuple[2] = resourceSelector; _keyTuple[3] = funcSelectorAndArgsHash; - _store.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + _store.deleteRecord(_tableId, _keyTuple, _fieldLayout); } } diff --git a/packages/world/src/modules/std-delegations/tables/TimeboundDelegations.sol b/packages/world/src/modules/std-delegations/tables/TimeboundDelegations.sol index 27a2552404..30a11520f3 100644 --- a/packages/world/src/modules/std-delegations/tables/TimeboundDelegations.sol +++ b/packages/world/src/modules/std-delegations/tables/TimeboundDelegations.sol @@ -21,13 +21,14 @@ import { PackedCounter, PackedCounterLib } from "@latticexyz/store/src/PackedCou bytes32 constant _tableId = bytes32(abi.encodePacked(bytes16(""), bytes16("TimeboundDelegat"))); bytes32 constant TimeboundDelegationsTableId = _tableId; +FieldLayout constant _fieldLayout = FieldLayout.wrap( + 0x0020010020000000000000000000000000000000000000000000000000000000 +); + library TimeboundDelegations { /** Get the table values' field layout */ function getFieldLayout() internal pure returns (FieldLayout) { - uint256[] memory _fieldLayout = new uint256[](1); - _fieldLayout[0] = 32; - - return FieldLayoutLib.encode(_fieldLayout, 0); + return _fieldLayout; } /** Get the table's key schema */ @@ -62,19 +63,17 @@ library TimeboundDelegations { /** Register the table with its config */ function register() internal { - StoreSwitch.registerTable( - _tableId, - getFieldLayout(), - getKeySchema(), - getValueSchema(), - getKeyNames(), - getFieldNames() - ); + StoreSwitch.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + } + + /** Register the table with its config */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Register the table with its config (using the specified store) */ function register(IStore _store) internal { - _store.registerTable(_tableId, getFieldLayout(), getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + _store.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Get maxTimestamp */ @@ -83,8 +82,18 @@ library TimeboundDelegations { _keyTuple[0] = bytes32(uint256(uint160(delegator))); _keyTuple[1] = bytes32(uint256(uint160(delegatee))); - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (uint256(Bytes.slice32(_blob, 0))); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint256(bytes32(_blob))); + } + + /** Get maxTimestamp */ + function _get(address delegator, address delegatee) internal view returns (uint256 maxTimestamp) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(uint160(delegator))); + _keyTuple[1] = bytes32(uint256(uint160(delegatee))); + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint256(bytes32(_blob))); } /** Get maxTimestamp (using the specified store) */ @@ -93,8 +102,8 @@ library TimeboundDelegations { _keyTuple[0] = bytes32(uint256(uint160(delegator))); _keyTuple[1] = bytes32(uint256(uint160(delegatee))); - bytes memory _blob = _store.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (uint256(Bytes.slice32(_blob, 0))); + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint256(bytes32(_blob))); } /** Set maxTimestamp */ @@ -103,7 +112,16 @@ library TimeboundDelegations { _keyTuple[0] = bytes32(uint256(uint160(delegator))); _keyTuple[1] = bytes32(uint256(uint160(delegatee))); - StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((maxTimestamp)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((maxTimestamp)), _fieldLayout); + } + + /** Set maxTimestamp */ + function _set(address delegator, address delegatee, uint256 maxTimestamp) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(uint160(delegator))); + _keyTuple[1] = bytes32(uint256(uint160(delegatee))); + + StoreCore.setField(_tableId, _keyTuple, 0, abi.encodePacked((maxTimestamp)), _fieldLayout); } /** Set maxTimestamp (using the specified store) */ @@ -112,12 +130,22 @@ library TimeboundDelegations { _keyTuple[0] = bytes32(uint256(uint160(delegator))); _keyTuple[1] = bytes32(uint256(uint160(delegatee))); - _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((maxTimestamp)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((maxTimestamp)), _fieldLayout); + } + + /** Tightly pack static data using this table's schema */ + function encodeStatic(uint256 maxTimestamp) internal pure returns (bytes memory) { + return abi.encodePacked(maxTimestamp); } /** Tightly pack full data using this table's field layout */ function encode(uint256 maxTimestamp) internal pure returns (bytes memory) { - return abi.encodePacked(maxTimestamp); + bytes memory _staticData = encodeStatic(maxTimestamp); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; + + return abi.encodePacked(_staticData, _encodedLengths, _dynamicData); } /** Encode keys as a bytes32 array using this table's field layout */ @@ -135,7 +163,16 @@ library TimeboundDelegations { _keyTuple[0] = bytes32(uint256(uint160(delegator))); _keyTuple[1] = bytes32(uint256(uint160(delegatee))); - StoreSwitch.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + StoreSwitch.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /* Delete all data for given keys */ + function _deleteRecord(address delegator, address delegatee) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(uint160(delegator))); + _keyTuple[1] = bytes32(uint256(uint160(delegatee))); + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); } /* Delete all data for given keys (using the specified store) */ @@ -144,6 +181,6 @@ library TimeboundDelegations { _keyTuple[0] = bytes32(uint256(uint160(delegator))); _keyTuple[1] = bytes32(uint256(uint160(delegatee))); - _store.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + _store.deleteRecord(_tableId, _keyTuple, _fieldLayout); } } diff --git a/packages/world/src/modules/uniqueentity/tables/UniqueEntity.sol b/packages/world/src/modules/uniqueentity/tables/UniqueEntity.sol index 092bde6646..7c0fc191e7 100644 --- a/packages/world/src/modules/uniqueentity/tables/UniqueEntity.sol +++ b/packages/world/src/modules/uniqueentity/tables/UniqueEntity.sol @@ -18,13 +18,14 @@ import { FieldLayout, FieldLayoutLib } from "@latticexyz/store/src/FieldLayout.s import { Schema, SchemaLib } from "@latticexyz/store/src/Schema.sol"; import { PackedCounter, PackedCounterLib } from "@latticexyz/store/src/PackedCounter.sol"; +FieldLayout constant _fieldLayout = FieldLayout.wrap( + 0x0020010020000000000000000000000000000000000000000000000000000000 +); + library UniqueEntity { /** Get the table values' field layout */ function getFieldLayout() internal pure returns (FieldLayout) { - uint256[] memory _fieldLayout = new uint256[](1); - _fieldLayout[0] = 32; - - return FieldLayoutLib.encode(_fieldLayout, 0); + return _fieldLayout; } /** Get the table's key schema */ @@ -55,54 +56,77 @@ library UniqueEntity { /** Register the table with its config */ function register(bytes32 _tableId) internal { - StoreSwitch.registerTable( - _tableId, - getFieldLayout(), - getKeySchema(), - getValueSchema(), - getKeyNames(), - getFieldNames() - ); + StoreSwitch.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + } + + /** Register the table with its config */ + function _register(bytes32 _tableId) internal { + StoreCore.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Register the table with its config (using the specified store) */ function register(IStore _store, bytes32 _tableId) internal { - _store.registerTable(_tableId, getFieldLayout(), getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + _store.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Get value */ function get(bytes32 _tableId) internal view returns (uint256 value) { bytes32[] memory _keyTuple = new bytes32[](0); - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (uint256(Bytes.slice32(_blob, 0))); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint256(bytes32(_blob))); + } + + /** Get value */ + function _get(bytes32 _tableId) internal view returns (uint256 value) { + bytes32[] memory _keyTuple = new bytes32[](0); + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint256(bytes32(_blob))); } /** Get value (using the specified store) */ function get(IStore _store, bytes32 _tableId) internal view returns (uint256 value) { bytes32[] memory _keyTuple = new bytes32[](0); - bytes memory _blob = _store.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (uint256(Bytes.slice32(_blob, 0))); + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint256(bytes32(_blob))); } /** Set value */ function set(bytes32 _tableId, uint256 value) internal { bytes32[] memory _keyTuple = new bytes32[](0); - StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((value)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); + } + + /** Set value */ + function _set(bytes32 _tableId, uint256 value) internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreCore.setField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); } /** Set value (using the specified store) */ function set(IStore _store, bytes32 _tableId, uint256 value) internal { bytes32[] memory _keyTuple = new bytes32[](0); - _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((value)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); + } + + /** Tightly pack static data using this table's schema */ + function encodeStatic(uint256 value) internal pure returns (bytes memory) { + return abi.encodePacked(value); } /** Tightly pack full data using this table's field layout */ function encode(uint256 value) internal pure returns (bytes memory) { - return abi.encodePacked(value); + bytes memory _staticData = encodeStatic(value); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; + + return abi.encodePacked(_staticData, _encodedLengths, _dynamicData); } /** Encode keys as a bytes32 array using this table's field layout */ @@ -116,13 +140,20 @@ library UniqueEntity { function deleteRecord(bytes32 _tableId) internal { bytes32[] memory _keyTuple = new bytes32[](0); - StoreSwitch.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + StoreSwitch.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /* Delete all data for given keys */ + function _deleteRecord(bytes32 _tableId) internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); } /* Delete all data for given keys (using the specified store) */ function deleteRecord(IStore _store, bytes32 _tableId) internal { bytes32[] memory _keyTuple = new bytes32[](0); - _store.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + _store.deleteRecord(_tableId, _keyTuple, _fieldLayout); } } diff --git a/packages/world/src/tables/Delegations.sol b/packages/world/src/tables/Delegations.sol index 5889c445bd..4226caa189 100644 --- a/packages/world/src/tables/Delegations.sol +++ b/packages/world/src/tables/Delegations.sol @@ -21,13 +21,14 @@ import { PackedCounter, PackedCounterLib } from "@latticexyz/store/src/PackedCou bytes32 constant _tableId = bytes32(abi.encodePacked(bytes16(""), bytes16("Delegations"))); bytes32 constant DelegationsTableId = _tableId; +FieldLayout constant _fieldLayout = FieldLayout.wrap( + 0x0020010020000000000000000000000000000000000000000000000000000000 +); + library Delegations { /** Get the table values' field layout */ function getFieldLayout() internal pure returns (FieldLayout) { - uint256[] memory _fieldLayout = new uint256[](1); - _fieldLayout[0] = 32; - - return FieldLayoutLib.encode(_fieldLayout, 0); + return _fieldLayout; } /** Get the table's key schema */ @@ -62,19 +63,17 @@ library Delegations { /** Register the table with its config */ function register() internal { - StoreSwitch.registerTable( - _tableId, - getFieldLayout(), - getKeySchema(), - getValueSchema(), - getKeyNames(), - getFieldNames() - ); + StoreSwitch.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + } + + /** Register the table with its config */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Register the table with its config (using the specified store) */ function register(IStore _store) internal { - _store.registerTable(_tableId, getFieldLayout(), getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + _store.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Get delegationControlId */ @@ -83,8 +82,18 @@ library Delegations { _keyTuple[0] = bytes32(uint256(uint160(delegator))); _keyTuple[1] = bytes32(uint256(uint160(delegatee))); - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (Bytes.slice32(_blob, 0)); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (bytes32(_blob)); + } + + /** Get delegationControlId */ + function _get(address delegator, address delegatee) internal view returns (bytes32 delegationControlId) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(uint160(delegator))); + _keyTuple[1] = bytes32(uint256(uint160(delegatee))); + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (bytes32(_blob)); } /** Get delegationControlId (using the specified store) */ @@ -97,8 +106,8 @@ library Delegations { _keyTuple[0] = bytes32(uint256(uint160(delegator))); _keyTuple[1] = bytes32(uint256(uint160(delegatee))); - bytes memory _blob = _store.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (Bytes.slice32(_blob, 0)); + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (bytes32(_blob)); } /** Set delegationControlId */ @@ -107,7 +116,16 @@ library Delegations { _keyTuple[0] = bytes32(uint256(uint160(delegator))); _keyTuple[1] = bytes32(uint256(uint160(delegatee))); - StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((delegationControlId)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((delegationControlId)), _fieldLayout); + } + + /** Set delegationControlId */ + function _set(address delegator, address delegatee, bytes32 delegationControlId) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(uint160(delegator))); + _keyTuple[1] = bytes32(uint256(uint160(delegatee))); + + StoreCore.setField(_tableId, _keyTuple, 0, abi.encodePacked((delegationControlId)), _fieldLayout); } /** Set delegationControlId (using the specified store) */ @@ -116,12 +134,22 @@ library Delegations { _keyTuple[0] = bytes32(uint256(uint160(delegator))); _keyTuple[1] = bytes32(uint256(uint160(delegatee))); - _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((delegationControlId)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((delegationControlId)), _fieldLayout); + } + + /** Tightly pack static data using this table's schema */ + function encodeStatic(bytes32 delegationControlId) internal pure returns (bytes memory) { + return abi.encodePacked(delegationControlId); } /** Tightly pack full data using this table's field layout */ function encode(bytes32 delegationControlId) internal pure returns (bytes memory) { - return abi.encodePacked(delegationControlId); + bytes memory _staticData = encodeStatic(delegationControlId); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; + + return abi.encodePacked(_staticData, _encodedLengths, _dynamicData); } /** Encode keys as a bytes32 array using this table's field layout */ @@ -139,7 +167,16 @@ library Delegations { _keyTuple[0] = bytes32(uint256(uint160(delegator))); _keyTuple[1] = bytes32(uint256(uint160(delegatee))); - StoreSwitch.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + StoreSwitch.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /* Delete all data for given keys */ + function _deleteRecord(address delegator, address delegatee) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(uint256(uint160(delegator))); + _keyTuple[1] = bytes32(uint256(uint160(delegatee))); + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); } /* Delete all data for given keys (using the specified store) */ @@ -148,6 +185,6 @@ library Delegations { _keyTuple[0] = bytes32(uint256(uint160(delegator))); _keyTuple[1] = bytes32(uint256(uint160(delegatee))); - _store.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + _store.deleteRecord(_tableId, _keyTuple, _fieldLayout); } } diff --git a/packages/world/src/tables/InstalledModules.sol b/packages/world/src/tables/InstalledModules.sol index a0bbbc61b7..530489f7a8 100644 --- a/packages/world/src/tables/InstalledModules.sol +++ b/packages/world/src/tables/InstalledModules.sol @@ -21,13 +21,14 @@ import { PackedCounter, PackedCounterLib } from "@latticexyz/store/src/PackedCou bytes32 constant _tableId = bytes32(abi.encodePacked(bytes16(""), bytes16("InstalledModules"))); bytes32 constant InstalledModulesTableId = _tableId; +FieldLayout constant _fieldLayout = FieldLayout.wrap( + 0x0014010014000000000000000000000000000000000000000000000000000000 +); + library InstalledModules { /** Get the table values' field layout */ function getFieldLayout() internal pure returns (FieldLayout) { - uint256[] memory _fieldLayout = new uint256[](1); - _fieldLayout[0] = 20; - - return FieldLayoutLib.encode(_fieldLayout, 0); + return _fieldLayout; } /** Get the table's key schema */ @@ -62,19 +63,17 @@ library InstalledModules { /** Register the table with its config */ function register() internal { - StoreSwitch.registerTable( - _tableId, - getFieldLayout(), - getKeySchema(), - getValueSchema(), - getKeyNames(), - getFieldNames() - ); + StoreSwitch.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + } + + /** Register the table with its config */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Register the table with its config (using the specified store) */ function register(IStore _store) internal { - _store.registerTable(_tableId, getFieldLayout(), getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + _store.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Get moduleAddress */ @@ -83,8 +82,18 @@ library InstalledModules { _keyTuple[0] = bytes32(moduleName); _keyTuple[1] = argumentsHash; - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (address(Bytes.slice20(_blob, 0))); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (address(bytes20(_blob))); + } + + /** Get moduleAddress */ + function _get(bytes16 moduleName, bytes32 argumentsHash) internal view returns (address moduleAddress) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(moduleName); + _keyTuple[1] = argumentsHash; + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (address(bytes20(_blob))); } /** Get moduleAddress (using the specified store) */ @@ -93,8 +102,8 @@ library InstalledModules { _keyTuple[0] = bytes32(moduleName); _keyTuple[1] = argumentsHash; - bytes memory _blob = _store.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (address(Bytes.slice20(_blob, 0))); + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (address(bytes20(_blob))); } /** Set moduleAddress */ @@ -103,7 +112,16 @@ library InstalledModules { _keyTuple[0] = bytes32(moduleName); _keyTuple[1] = argumentsHash; - StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((moduleAddress)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((moduleAddress)), _fieldLayout); + } + + /** Set moduleAddress */ + function _set(bytes16 moduleName, bytes32 argumentsHash, address moduleAddress) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(moduleName); + _keyTuple[1] = argumentsHash; + + StoreCore.setField(_tableId, _keyTuple, 0, abi.encodePacked((moduleAddress)), _fieldLayout); } /** Set moduleAddress (using the specified store) */ @@ -112,12 +130,22 @@ library InstalledModules { _keyTuple[0] = bytes32(moduleName); _keyTuple[1] = argumentsHash; - _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((moduleAddress)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((moduleAddress)), _fieldLayout); + } + + /** Tightly pack static data using this table's schema */ + function encodeStatic(address moduleAddress) internal pure returns (bytes memory) { + return abi.encodePacked(moduleAddress); } /** Tightly pack full data using this table's field layout */ function encode(address moduleAddress) internal pure returns (bytes memory) { - return abi.encodePacked(moduleAddress); + bytes memory _staticData = encodeStatic(moduleAddress); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; + + return abi.encodePacked(_staticData, _encodedLengths, _dynamicData); } /** Encode keys as a bytes32 array using this table's field layout */ @@ -135,7 +163,16 @@ library InstalledModules { _keyTuple[0] = bytes32(moduleName); _keyTuple[1] = argumentsHash; - StoreSwitch.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + StoreSwitch.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /* Delete all data for given keys */ + function _deleteRecord(bytes16 moduleName, bytes32 argumentsHash) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = bytes32(moduleName); + _keyTuple[1] = argumentsHash; + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); } /* Delete all data for given keys (using the specified store) */ @@ -144,6 +181,6 @@ library InstalledModules { _keyTuple[0] = bytes32(moduleName); _keyTuple[1] = argumentsHash; - _store.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + _store.deleteRecord(_tableId, _keyTuple, _fieldLayout); } } diff --git a/packages/world/src/tables/NamespaceOwner.sol b/packages/world/src/tables/NamespaceOwner.sol index 54033da9dd..5b7946f7a9 100644 --- a/packages/world/src/tables/NamespaceOwner.sol +++ b/packages/world/src/tables/NamespaceOwner.sol @@ -21,13 +21,14 @@ import { PackedCounter, PackedCounterLib } from "@latticexyz/store/src/PackedCou bytes32 constant _tableId = bytes32(abi.encodePacked(bytes16(""), bytes16("NamespaceOwner"))); bytes32 constant NamespaceOwnerTableId = _tableId; +FieldLayout constant _fieldLayout = FieldLayout.wrap( + 0x0014010014000000000000000000000000000000000000000000000000000000 +); + library NamespaceOwner { /** Get the table values' field layout */ function getFieldLayout() internal pure returns (FieldLayout) { - uint256[] memory _fieldLayout = new uint256[](1); - _fieldLayout[0] = 20; - - return FieldLayoutLib.encode(_fieldLayout, 0); + return _fieldLayout; } /** Get the table's key schema */ @@ -60,19 +61,17 @@ library NamespaceOwner { /** Register the table with its config */ function register() internal { - StoreSwitch.registerTable( - _tableId, - getFieldLayout(), - getKeySchema(), - getValueSchema(), - getKeyNames(), - getFieldNames() - ); + StoreSwitch.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + } + + /** Register the table with its config */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Register the table with its config (using the specified store) */ function register(IStore _store) internal { - _store.registerTable(_tableId, getFieldLayout(), getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + _store.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Get owner */ @@ -80,8 +79,17 @@ library NamespaceOwner { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(namespace); - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (address(Bytes.slice20(_blob, 0))); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (address(bytes20(_blob))); + } + + /** Get owner */ + function _get(bytes16 namespace) internal view returns (address owner) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(namespace); + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (address(bytes20(_blob))); } /** Get owner (using the specified store) */ @@ -89,8 +97,8 @@ library NamespaceOwner { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(namespace); - bytes memory _blob = _store.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (address(Bytes.slice20(_blob, 0))); + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (address(bytes20(_blob))); } /** Set owner */ @@ -98,7 +106,15 @@ library NamespaceOwner { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(namespace); - StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((owner)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((owner)), _fieldLayout); + } + + /** Set owner */ + function _set(bytes16 namespace, address owner) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(namespace); + + StoreCore.setField(_tableId, _keyTuple, 0, abi.encodePacked((owner)), _fieldLayout); } /** Set owner (using the specified store) */ @@ -106,12 +122,22 @@ library NamespaceOwner { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(namespace); - _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((owner)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((owner)), _fieldLayout); + } + + /** Tightly pack static data using this table's schema */ + function encodeStatic(address owner) internal pure returns (bytes memory) { + return abi.encodePacked(owner); } /** Tightly pack full data using this table's field layout */ function encode(address owner) internal pure returns (bytes memory) { - return abi.encodePacked(owner); + bytes memory _staticData = encodeStatic(owner); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; + + return abi.encodePacked(_staticData, _encodedLengths, _dynamicData); } /** Encode keys as a bytes32 array using this table's field layout */ @@ -127,7 +153,15 @@ library NamespaceOwner { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(namespace); - StoreSwitch.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + StoreSwitch.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /* Delete all data for given keys */ + function _deleteRecord(bytes16 namespace) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(namespace); + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); } /* Delete all data for given keys (using the specified store) */ @@ -135,6 +169,6 @@ library NamespaceOwner { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = bytes32(namespace); - _store.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + _store.deleteRecord(_tableId, _keyTuple, _fieldLayout); } } diff --git a/packages/world/src/tables/ResourceAccess.sol b/packages/world/src/tables/ResourceAccess.sol index f53abae8e7..9d591d49ef 100644 --- a/packages/world/src/tables/ResourceAccess.sol +++ b/packages/world/src/tables/ResourceAccess.sol @@ -21,13 +21,14 @@ import { PackedCounter, PackedCounterLib } from "@latticexyz/store/src/PackedCou bytes32 constant _tableId = bytes32(abi.encodePacked(bytes16(""), bytes16("ResourceAccess"))); bytes32 constant ResourceAccessTableId = _tableId; +FieldLayout constant _fieldLayout = FieldLayout.wrap( + 0x0001010001000000000000000000000000000000000000000000000000000000 +); + library ResourceAccess { /** Get the table values' field layout */ function getFieldLayout() internal pure returns (FieldLayout) { - uint256[] memory _fieldLayout = new uint256[](1); - _fieldLayout[0] = 1; - - return FieldLayoutLib.encode(_fieldLayout, 0); + return _fieldLayout; } /** Get the table's key schema */ @@ -62,19 +63,17 @@ library ResourceAccess { /** Register the table with its config */ function register() internal { - StoreSwitch.registerTable( - _tableId, - getFieldLayout(), - getKeySchema(), - getValueSchema(), - getKeyNames(), - getFieldNames() - ); + StoreSwitch.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + } + + /** Register the table with its config */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Register the table with its config (using the specified store) */ function register(IStore _store) internal { - _store.registerTable(_tableId, getFieldLayout(), getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + _store.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Get access */ @@ -83,8 +82,18 @@ library ResourceAccess { _keyTuple[0] = resourceSelector; _keyTuple[1] = bytes32(uint256(uint160(caller))); - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (_toBool(uint8(Bytes.slice1(_blob, 0)))); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (_toBool(uint8(bytes1(_blob)))); + } + + /** Get access */ + function _get(bytes32 resourceSelector, address caller) internal view returns (bool access) { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = resourceSelector; + _keyTuple[1] = bytes32(uint256(uint160(caller))); + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (_toBool(uint8(bytes1(_blob)))); } /** Get access (using the specified store) */ @@ -93,8 +102,8 @@ library ResourceAccess { _keyTuple[0] = resourceSelector; _keyTuple[1] = bytes32(uint256(uint160(caller))); - bytes memory _blob = _store.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (_toBool(uint8(Bytes.slice1(_blob, 0)))); + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (_toBool(uint8(bytes1(_blob)))); } /** Set access */ @@ -103,7 +112,16 @@ library ResourceAccess { _keyTuple[0] = resourceSelector; _keyTuple[1] = bytes32(uint256(uint160(caller))); - StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((access)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((access)), _fieldLayout); + } + + /** Set access */ + function _set(bytes32 resourceSelector, address caller, bool access) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = resourceSelector; + _keyTuple[1] = bytes32(uint256(uint160(caller))); + + StoreCore.setField(_tableId, _keyTuple, 0, abi.encodePacked((access)), _fieldLayout); } /** Set access (using the specified store) */ @@ -112,12 +130,22 @@ library ResourceAccess { _keyTuple[0] = resourceSelector; _keyTuple[1] = bytes32(uint256(uint160(caller))); - _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((access)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((access)), _fieldLayout); + } + + /** Tightly pack static data using this table's schema */ + function encodeStatic(bool access) internal pure returns (bytes memory) { + return abi.encodePacked(access); } /** Tightly pack full data using this table's field layout */ function encode(bool access) internal pure returns (bytes memory) { - return abi.encodePacked(access); + bytes memory _staticData = encodeStatic(access); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; + + return abi.encodePacked(_staticData, _encodedLengths, _dynamicData); } /** Encode keys as a bytes32 array using this table's field layout */ @@ -135,7 +163,16 @@ library ResourceAccess { _keyTuple[0] = resourceSelector; _keyTuple[1] = bytes32(uint256(uint160(caller))); - StoreSwitch.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + StoreSwitch.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /* Delete all data for given keys */ + function _deleteRecord(bytes32 resourceSelector, address caller) internal { + bytes32[] memory _keyTuple = new bytes32[](2); + _keyTuple[0] = resourceSelector; + _keyTuple[1] = bytes32(uint256(uint160(caller))); + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); } /* Delete all data for given keys (using the specified store) */ @@ -144,7 +181,7 @@ library ResourceAccess { _keyTuple[0] = resourceSelector; _keyTuple[1] = bytes32(uint256(uint160(caller))); - _store.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + _store.deleteRecord(_tableId, _keyTuple, _fieldLayout); } } diff --git a/packages/world/test/KeysInTableModule.t.sol b/packages/world/test/KeysInTableModule.t.sol index e25f7b5427..7789e33740 100644 --- a/packages/world/test/KeysInTableModule.t.sol +++ b/packages/world/test/KeysInTableModule.t.sol @@ -7,6 +7,7 @@ import { GasReporter } from "@latticexyz/gas-report/src/GasReporter.sol"; import { FieldLayout, FieldLayoutLib } from "@latticexyz/store/src/FieldLayout.sol"; import { FieldLayoutEncodeHelper } from "@latticexyz/store/test/FieldLayoutEncodeHelper.sol"; import { Schema, SchemaLib } from "@latticexyz/store/src/Schema.sol"; +import { PackedCounter } from "@latticexyz/store/src/PackedCounter.sol"; import { SchemaEncodeHelper } from "@latticexyz/store/test/SchemaEncodeHelper.sol"; import { SchemaType } from "@latticexyz/schema-type/src/solidity/SchemaType.sol"; @@ -103,7 +104,14 @@ contract KeysInTableModuleTest is Test, GasReporter { bytes32[] memory keyTuple = new bytes32[](0); - world.setRecord(singletonTableId, keyTuple, abi.encodePacked(val1), tableFieldLayout); + world.setRecord( + singletonTableId, + keyTuple, + abi.encodePacked(val1), + PackedCounter.wrap(bytes32(0)), + new bytes(0), + tableFieldLayout + ); // Get the list of keys in this target table bytes32[][] memory keysInTable = getKeysInTable(world, singletonTableId); @@ -120,7 +128,14 @@ contract KeysInTableModuleTest is Test, GasReporter { keyTuple[1] = "two"; keyTuple[2] = "three"; - world.setRecord(compositeTableId, keyTuple, abi.encodePacked(val1), tableFieldLayout); + world.setRecord( + compositeTableId, + keyTuple, + abi.encodePacked(val1), + PackedCounter.wrap(bytes32(0)), + new bytes(0), + tableFieldLayout + ); // Get the list of keys in this target table bytes32[][] memory keysInTable = getKeysInTable(world, compositeTableId); @@ -145,7 +160,14 @@ contract KeysInTableModuleTest is Test, GasReporter { _installKeysInTableModule(); // Set a value in the source table startGasReport("set a record on a table with keysInTableModule installed"); - world.setRecord(tableId, keyTuple1, abi.encodePacked(value), tableFieldLayout); + world.setRecord( + tableId, + keyTuple1, + abi.encodePacked(value), + PackedCounter.wrap(bytes32(0)), + new bytes(0), + tableFieldLayout + ); endGasReport(); // Get the list of keys in this target table @@ -164,7 +186,14 @@ contract KeysInTableModuleTest is Test, GasReporter { // Set a value in the source table startGasReport("set a record on a table with keysInTableModule installed (first)"); - world.setRecord(tableId, keyTuple, abi.encodePacked(value1), tableFieldLayout); + world.setRecord( + tableId, + keyTuple, + abi.encodePacked(value1), + PackedCounter.wrap(bytes32(0)), + new bytes(0), + tableFieldLayout + ); endGasReport(); // Get the list of keys in the first target table @@ -192,7 +221,14 @@ contract KeysInTableModuleTest is Test, GasReporter { // Set a value in the source table startGasReport("set a record on a table with keysInTableModule installed (second)"); - world.setRecord(sourceTableId2, keyTuple, abi.encodePacked(value2), tableFieldLayout); + world.setRecord( + sourceTableId2, + keyTuple, + abi.encodePacked(value2), + PackedCounter.wrap(bytes32(0)), + new bytes(0), + tableFieldLayout + ); endGasReport(); // Get the list of keys in the second target table @@ -212,7 +248,14 @@ contract KeysInTableModuleTest is Test, GasReporter { _installKeysInTableModule(); // Set a value in the source table - world.setRecord(tableId, keyTuple1, abi.encodePacked(value1), tableFieldLayout); + world.setRecord( + tableId, + keyTuple1, + abi.encodePacked(value1), + PackedCounter.wrap(bytes32(0)), + new bytes(0), + tableFieldLayout + ); // Get the list of keys in the target table bytes32[][] memory keysInTable = getKeysInTable(world, tableId); @@ -222,7 +265,14 @@ contract KeysInTableModuleTest is Test, GasReporter { assertEq(keysInTable[0][0], key1, "2"); // Set another key with the same value - world.setRecord(tableId, keyTuple2, abi.encodePacked(value1), tableFieldLayout); + world.setRecord( + tableId, + keyTuple2, + abi.encodePacked(value1), + PackedCounter.wrap(bytes32(0)), + new bytes(0), + tableFieldLayout + ); // Get the list of keys in the target table keysInTable = getKeysInTable(world, tableId); @@ -234,7 +284,14 @@ contract KeysInTableModuleTest is Test, GasReporter { // Change the value of the first key startGasReport("change a record on a table with keysInTableModule installed"); - world.setRecord(tableId, keyTuple1, abi.encodePacked(value2), tableFieldLayout); + world.setRecord( + tableId, + keyTuple1, + abi.encodePacked(value2), + PackedCounter.wrap(bytes32(0)), + new bytes(0), + tableFieldLayout + ); endGasReport(); // Get the list of keys in the target table @@ -277,7 +334,14 @@ contract KeysInTableModuleTest is Test, GasReporter { keyTupleB[2] = "charlie"; // Set a value in the source table - world.setRecord(compositeTableId, keyTupleA, abi.encodePacked(value1), tableFieldLayout); + world.setRecord( + compositeTableId, + keyTupleA, + abi.encodePacked(value1), + PackedCounter.wrap(bytes32(0)), + new bytes(0), + tableFieldLayout + ); // Get the list of keys in the target table bytes32[][] memory keysInTable = getKeysInTable(world, compositeTableId); @@ -289,7 +353,14 @@ contract KeysInTableModuleTest is Test, GasReporter { } // Set another key with the same value - world.setRecord(compositeTableId, keyTupleB, abi.encodePacked(value1), tableFieldLayout); + world.setRecord( + compositeTableId, + keyTupleB, + abi.encodePacked(value1), + PackedCounter.wrap(bytes32(0)), + new bytes(0), + tableFieldLayout + ); // Get the list of keys in the target table keysInTable = getKeysInTable(world, compositeTableId); @@ -305,7 +376,14 @@ contract KeysInTableModuleTest is Test, GasReporter { // Change the value of the first key startGasReport("change a composite record on a table with keysInTableModule installed"); - world.setRecord(compositeTableId, keyTupleA, abi.encodePacked(value2), tableFieldLayout); + world.setRecord( + compositeTableId, + keyTupleA, + abi.encodePacked(value2), + PackedCounter.wrap(bytes32(0)), + new bytes(0), + tableFieldLayout + ); endGasReport(); // Get the list of keys in the target table @@ -367,7 +445,14 @@ contract KeysInTableModuleTest is Test, GasReporter { _installKeysInTableModule(); // Set a value in the source table - world.setRecord(tableId, keyTuple1, abi.encodePacked(value1), tableFieldLayout); + world.setRecord( + tableId, + keyTuple1, + abi.encodePacked(value1), + PackedCounter.wrap(bytes32(0)), + new bytes(0), + tableFieldLayout + ); startGasReport("Get list of keys in a given table"); bytes32[][] memory keysInTable = getKeysInTable(world, tableId); @@ -378,7 +463,14 @@ contract KeysInTableModuleTest is Test, GasReporter { assertEq(keysInTable[0][0], key1); // Set another key with a different value - world.setRecord(tableId, keyTuple2, abi.encodePacked(value2), tableFieldLayout); + world.setRecord( + tableId, + keyTuple2, + abi.encodePacked(value2), + PackedCounter.wrap(bytes32(0)), + new bytes(0), + tableFieldLayout + ); // Get the list of keys in the target table keysInTable = getKeysInTable(world, tableId); @@ -393,9 +485,30 @@ contract KeysInTableModuleTest is Test, GasReporter { _installKeysInTableModule(); // Add 3 values - world.setRecord(tableId, keyTuple1, abi.encodePacked(value), tableFieldLayout); - world.setRecord(tableId, keyTuple2, abi.encodePacked(value), tableFieldLayout); - world.setRecord(tableId, keyTuple3, abi.encodePacked(value), tableFieldLayout); + world.setRecord( + tableId, + keyTuple1, + abi.encodePacked(value), + PackedCounter.wrap(bytes32(0)), + new bytes(0), + tableFieldLayout + ); + world.setRecord( + tableId, + keyTuple2, + abi.encodePacked(value), + PackedCounter.wrap(bytes32(0)), + new bytes(0), + tableFieldLayout + ); + world.setRecord( + tableId, + keyTuple3, + abi.encodePacked(value), + PackedCounter.wrap(bytes32(0)), + new bytes(0), + tableFieldLayout + ); // Remove 2, starting from the middle // This tests that KeysInTable correctly tracks swaps indexes diff --git a/packages/world/test/KeysWithValueModule.t.sol b/packages/world/test/KeysWithValueModule.t.sol index 50307d969b..fa8e8e83d5 100644 --- a/packages/world/test/KeysWithValueModule.t.sol +++ b/packages/world/test/KeysWithValueModule.t.sol @@ -1,10 +1,11 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; -import { Test } from "forge-std/Test.sol"; +import { Test, console } from "forge-std/Test.sol"; import { GasReporter } from "@latticexyz/gas-report/src/GasReporter.sol"; import { Schema } from "@latticexyz/store/src/Schema.sol"; +import { PackedCounter } from "@latticexyz/store/src/PackedCounter.sol"; import { SchemaEncodeHelper } from "@latticexyz/store/test/SchemaEncodeHelper.sol"; import { SchemaType } from "@latticexyz/schema-type/src/solidity/SchemaType.sol"; @@ -26,20 +27,20 @@ import { getTargetTableSelector } from "../src/modules/utils/getTargetTableSelec contract KeysWithValueModuleTest is Test, GasReporter { using ResourceSelector for bytes32; IBaseWorld world; - KeysWithValueModule keysWithValueModule = new KeysWithValueModule(); // Modules can be deployed once and installed multiple times + KeysWithValueModule private keysWithValueModule = new KeysWithValueModule(); // Modules can be deployed once and installed multiple times - bytes16 namespace = ROOT_NAMESPACE; - bytes16 sourceName = bytes16("source"); - bytes32 key1 = keccak256("test"); - bytes32[] keyTuple1; - bytes32 key2 = keccak256("test2"); - bytes32[] keyTuple2; + bytes16 private namespace = ROOT_NAMESPACE; + bytes16 private sourceName = bytes16("source"); + bytes32 private key1 = keccak256("test"); + bytes32[] private keyTuple1; + bytes32 private key2 = keccak256("test2"); + bytes32[] private keyTuple2; - FieldLayout sourceTableFieldLayout; - Schema sourceTableSchema; - Schema sourceTableKeySchema; - bytes32 sourceTableId; - bytes32 targetTableId; + FieldLayout private sourceTableFieldLayout; + Schema private sourceTableSchema; + Schema private sourceTableKeySchema; + bytes32 private sourceTableId; + bytes32 private targetTableId; function setUp() public { sourceTableFieldLayout = FieldLayoutEncodeHelper.encode(32, 0); @@ -80,11 +81,18 @@ contract KeysWithValueModuleTest is Test, GasReporter { uint256 value = 1; startGasReport("set a record on a table with KeysWithValueModule installed"); - world.setRecord(sourceTableId, keyTuple1, abi.encodePacked(value), sourceTableFieldLayout); + world.setRecord( + sourceTableId, + keyTuple1, + abi.encodePacked(value), + PackedCounter.wrap(bytes32(0)), + new bytes(0), + sourceTableFieldLayout + ); endGasReport(); // Get the list of entities with this value from the target table - bytes32[] memory keysWithValue = KeysWithValue.get(world, targetTableId, keccak256(abi.encode(value))); + bytes32[] memory keysWithValue = KeysWithValue.get(world, targetTableId, keccak256(abi.encodePacked(value))); // Assert that the list is correct assertEq(keysWithValue.length, 1); @@ -97,20 +105,34 @@ contract KeysWithValueModuleTest is Test, GasReporter { // Set a value in the source table uint256 value1 = 1; - world.setRecord(sourceTableId, keyTuple1, abi.encodePacked(value1), sourceTableFieldLayout); + world.setRecord( + sourceTableId, + keyTuple1, + abi.encodePacked(value1), + PackedCounter.wrap(bytes32(0)), + new bytes(0), + sourceTableFieldLayout + ); // Get the list of entities with value1 from the target table - bytes32[] memory keysWithValue = KeysWithValue.get(world, targetTableId, keccak256(abi.encode(value1))); + bytes32[] memory keysWithValue = KeysWithValue.get(world, targetTableId, keccak256(abi.encodePacked(value1))); // Assert that the list is correct assertEq(keysWithValue.length, 1, "1"); assertEq(keysWithValue[0], key1, "2"); // Set a another key with the same value - world.setRecord(sourceTableId, keyTuple2, abi.encodePacked(value1), sourceTableFieldLayout); + world.setRecord( + sourceTableId, + keyTuple2, + abi.encodePacked(value1), + PackedCounter.wrap(bytes32(0)), + new bytes(0), + sourceTableFieldLayout + ); // Get the list of entities with value2 from the target table - keysWithValue = KeysWithValue.get(world, targetTableId, keccak256(abi.encode(value1))); + keysWithValue = KeysWithValue.get(world, targetTableId, keccak256(abi.encodePacked(value1))); // Assert that the list is correct assertEq(keysWithValue.length, 2); @@ -121,18 +143,25 @@ contract KeysWithValueModuleTest is Test, GasReporter { uint256 value2 = 2; startGasReport("change a record on a table with KeysWithValueModule installed"); - world.setRecord(sourceTableId, keyTuple1, abi.encodePacked(value2), sourceTableFieldLayout); + world.setRecord( + sourceTableId, + keyTuple1, + abi.encodePacked(value2), + PackedCounter.wrap(bytes32(0)), + new bytes(0), + sourceTableFieldLayout + ); endGasReport(); // Get the list of entities with value1 from the target table - keysWithValue = KeysWithValue.get(world, targetTableId, keccak256(abi.encode(value1))); + keysWithValue = KeysWithValue.get(world, targetTableId, keccak256(abi.encodePacked(value1))); // Assert that the list is correct assertEq(keysWithValue.length, 1, "5"); assertEq(keysWithValue[0], key2, "6"); // Get the list of entities with value2 from the target table - keysWithValue = KeysWithValue.get(world, targetTableId, keccak256(abi.encode(value2))); + keysWithValue = KeysWithValue.get(world, targetTableId, keccak256(abi.encodePacked(value2))); // Assert that the list is correct assertEq(keysWithValue.length, 1, "7"); @@ -144,7 +173,7 @@ contract KeysWithValueModuleTest is Test, GasReporter { endGasReport(); // Get the list of entities with value2 from the target table - keysWithValue = KeysWithValue.get(world, targetTableId, keccak256(abi.encode(value2))); + keysWithValue = KeysWithValue.get(world, targetTableId, keccak256(abi.encodePacked(value2))); // Assert that the list is correct assertEq(keysWithValue.length, 0, "9"); @@ -212,10 +241,23 @@ contract KeysWithValueModuleTest is Test, GasReporter { _installKeysWithValueModule(); // Set a value in the source table - world.setRecord(sourceTableId, keyTuple1, abi.encodePacked(value), sourceTableFieldLayout); + world.setRecord( + sourceTableId, + keyTuple1, + abi.encodePacked(value), + PackedCounter.wrap(bytes32(0)), + new bytes(0), + sourceTableFieldLayout + ); startGasReport("Get list of keys with a given value"); - bytes32[] memory keysWithValue = getKeysWithValue(world, sourceTableId, abi.encode(value)); + bytes32[] memory keysWithValue = getKeysWithValue( + world, + sourceTableId, + abi.encodePacked(value), + PackedCounter.wrap(bytes32(0)), + new bytes(0) + ); endGasReport(); // Assert that the list is correct @@ -223,10 +265,23 @@ contract KeysWithValueModuleTest is Test, GasReporter { assertEq(keysWithValue[0], key1); // Set a another key with the same value - world.setRecord(sourceTableId, keyTuple2, abi.encodePacked(value), sourceTableFieldLayout); + world.setRecord( + sourceTableId, + keyTuple2, + abi.encodePacked(value), + PackedCounter.wrap(bytes32(0)), + new bytes(0), + sourceTableFieldLayout + ); // Get the list of keys with value from the target table - keysWithValue = getKeysWithValue(world, sourceTableId, abi.encode(value)); + keysWithValue = getKeysWithValue( + world, + sourceTableId, + abi.encodePacked(value), + PackedCounter.wrap(bytes32(0)), + new bytes(0) + ); // Assert that the list is correct assertEq(keysWithValue.length, 2); diff --git a/packages/world/test/World.t.sol b/packages/world/test/World.t.sol index 1d168ead9a..9d651d3ef2 100644 --- a/packages/world/test/World.t.sol +++ b/packages/world/test/World.t.sol @@ -12,6 +12,7 @@ import { StoreSwitch } from "@latticexyz/store/src/StoreSwitch.sol"; import { FieldLayout, FieldLayoutLib } from "@latticexyz/store/src/FieldLayout.sol"; import { FieldLayoutEncodeHelper } from "@latticexyz/store/test/FieldLayoutEncodeHelper.sol"; import { Schema, SchemaLib } from "@latticexyz/store/src/Schema.sol"; +import { PackedCounter } from "@latticexyz/store/src/PackedCounter.sol"; import { SchemaEncodeHelper } from "@latticexyz/store/test/SchemaEncodeHelper.sol"; import { Tables, TablesTableId } from "@latticexyz/store/src/codegen/Tables.sol"; import { EncodeArray } from "@latticexyz/store/src/tightcoder/EncodeArray.sol"; @@ -91,9 +92,23 @@ contract WorldTestSystem is System { FieldLayout fieldLayout = StoreSwitch.getFieldLayout(tableId); if (StoreSwitch.getStoreAddress() == address(this)) { - StoreCore.setRecord(tableId, keyTuple, abi.encodePacked(data), fieldLayout); + StoreCore.setRecord( + tableId, + keyTuple, + abi.encodePacked(data), + PackedCounter.wrap(bytes32(0)), + new bytes(0), + fieldLayout + ); } else { - IBaseWorld(msg.sender).setRecord(tableId, keyTuple, abi.encodePacked(data), fieldLayout); + IBaseWorld(msg.sender).setRecord( + tableId, + keyTuple, + abi.encodePacked(data), + PackedCounter.wrap(bytes32(0)), + new bytes(0), + fieldLayout + ); } } @@ -621,7 +636,14 @@ contract WorldTest is Test, GasReporter { world.registerTable(tableId, fieldLayout, defaultKeySchema, valueSchema, new string[](1), new string[](1)); // Write data to the table and expect it to be written - world.setRecord(tableId, singletonKey, abi.encodePacked(true), fieldLayout); + world.setRecord( + tableId, + singletonKey, + abi.encodePacked(true), + PackedCounter.wrap(bytes32(0)), + new bytes(0), + fieldLayout + ); assertTrue(Bool.get(world, tableId)); startGasReport("Delete record"); @@ -632,7 +654,15 @@ contract WorldTest is Test, GasReporter { assertFalse(Bool.get(world, tableId)); // Write data to the table and expect it to be written - world.setRecord(tableId, singletonKey, abi.encodePacked(true), fieldLayout); + world.setRecord( + tableId, + singletonKey, + abi.encodePacked(true), + PackedCounter.wrap(bytes32(0)), + new bytes(0), + fieldLayout + ); + assertTrue(Bool.get(world, tableId)); assertTrue(Bool.get(world, tableId)); // Expect an error when trying to delete from an address that doesn't have access @@ -822,25 +852,29 @@ contract WorldTest is Test, GasReporter { ); // Prepare data to write to the table - bytes memory value = abi.encodePacked(true); + bytes memory staticData = abi.encodePacked(true); // Expect the hook to be notified when a record is written (once before and once after the record is written) vm.expectEmit(true, true, true, true); - emit HookCalled(abi.encode(tableId, singletonKey, value, fieldLayout)); + emit HookCalled( + abi.encode(tableId, singletonKey, staticData, PackedCounter.wrap(bytes32(0)), new bytes(0), fieldLayout) + ); vm.expectEmit(true, true, true, true); - emit HookCalled(abi.encode(tableId, singletonKey, value, fieldLayout)); + emit HookCalled( + abi.encode(tableId, singletonKey, staticData, PackedCounter.wrap(bytes32(0)), new bytes(0), fieldLayout) + ); - world.setRecord(tableId, singletonKey, value, fieldLayout); + world.setRecord(tableId, singletonKey, staticData, PackedCounter.wrap(bytes32(0)), new bytes(0), fieldLayout); // Expect the hook to be notified when a field is written (once before and once after the field is written) vm.expectEmit(true, true, true, true); - emit HookCalled(abi.encode(tableId, singletonKey, uint8(0), value, fieldLayout)); + emit HookCalled(abi.encode(tableId, singletonKey, uint8(0), staticData, fieldLayout)); vm.expectEmit(true, true, true, true); - emit HookCalled(abi.encode(tableId, singletonKey, uint8(0), value, fieldLayout)); + emit HookCalled(abi.encode(tableId, singletonKey, uint8(0), staticData, fieldLayout)); - world.setField(tableId, singletonKey, 0, value, fieldLayout); + world.setField(tableId, singletonKey, 0, staticData, fieldLayout); // Expect the hook to be notified when a record is deleted (once before and once after the field is written) vm.expectEmit(true, true, true, true); @@ -907,15 +941,15 @@ contract WorldTest is Test, GasReporter { ); // Prepare data to write to the table - bytes memory value = abi.encodePacked(true); + bytes memory staticData = abi.encodePacked(true); // Expect a revert when the RevertSubscriber's onBeforeSetRecord hook is called vm.expectRevert(bytes("onBeforeSetRecord")); - world.setRecord(tableId, singletonKey, value, fieldLayout); + world.setRecord(tableId, singletonKey, staticData, PackedCounter.wrap(bytes32(0)), new bytes(0), fieldLayout); // Expect a revert when the RevertSubscriber's onBeforeSetField hook is called vm.expectRevert(bytes("onBeforeSetField")); - world.setField(tableId, singletonKey, 0, value, fieldLayout); + world.setField(tableId, singletonKey, 0, staticData, fieldLayout); // Expect a revert when the RevertSubscriber's onBeforeDeleteRecord hook is called vm.expectRevert(bytes("onBeforeDeleteRecord")); @@ -926,21 +960,25 @@ contract WorldTest is Test, GasReporter { // Expect the hook to be notified when a record is written (once before and once after the record is written) vm.expectEmit(true, true, true, true); - emit HookCalled(abi.encode(tableId, singletonKey, value, fieldLayout)); + emit HookCalled( + abi.encode(tableId, singletonKey, staticData, PackedCounter.wrap(bytes32(0)), new bytes(0), fieldLayout) + ); vm.expectEmit(true, true, true, true); - emit HookCalled(abi.encode(tableId, singletonKey, value, fieldLayout)); + emit HookCalled( + abi.encode(tableId, singletonKey, staticData, PackedCounter.wrap(bytes32(0)), new bytes(0), fieldLayout) + ); - world.setRecord(tableId, singletonKey, value, fieldLayout); + world.setRecord(tableId, singletonKey, staticData, PackedCounter.wrap(bytes32(0)), new bytes(0), fieldLayout); // Expect the hook to be notified when a field is written (once before and once after the field is written) vm.expectEmit(true, true, true, true); - emit HookCalled(abi.encode(tableId, singletonKey, uint8(0), value, fieldLayout)); + emit HookCalled(abi.encode(tableId, singletonKey, uint8(0), staticData, fieldLayout)); vm.expectEmit(true, true, true, true); - emit HookCalled(abi.encode(tableId, singletonKey, uint8(0), value, fieldLayout)); + emit HookCalled(abi.encode(tableId, singletonKey, uint8(0), staticData, fieldLayout)); - world.setField(tableId, singletonKey, 0, value, fieldLayout); + world.setField(tableId, singletonKey, 0, staticData, fieldLayout); // Expect the hook to be notified when a record is deleted (once before and once after the field is written) vm.expectEmit(true, true, true, true); diff --git a/packages/world/test/query.t.sol b/packages/world/test/query.t.sol index 0d7b3b63f8..27f8621b9a 100644 --- a/packages/world/test/query.t.sol +++ b/packages/world/test/query.t.sol @@ -8,6 +8,7 @@ import { FieldLayout } from "@latticexyz/store/src/FieldLayout.sol"; import { FieldLayoutEncodeHelper } from "@latticexyz/store/test/FieldLayoutEncodeHelper.sol"; import { Schema } from "@latticexyz/store/src/Schema.sol"; import { SchemaEncodeHelper } from "@latticexyz/store/test/SchemaEncodeHelper.sol"; +import { PackedCounter } from "@latticexyz/store/src/PackedCounter.sol"; import { SchemaType } from "@latticexyz/schema-type/src/solidity/SchemaType.sol"; import { World } from "../src/World.sol"; @@ -22,29 +23,29 @@ import { query, QueryFragment, QueryType } from "../src/modules/keysintable/quer contract QueryTest is Test, GasReporter { using ResourceSelector for bytes32; - IBaseWorld world; - KeysInTableModule keysInTableModule = new KeysInTableModule(); // Modules can be deployed once and installed multiple times - KeysWithValueModule keysWithValueModule = new KeysWithValueModule(); - - bytes16 namespace = ROOT_NAMESPACE; - bytes16 name1 = bytes16("source1"); - bytes16 name2 = bytes16("source2"); - bytes16 name3 = bytes16("source3"); - - FieldLayout tableFieldLayout; - Schema tableKeySchema; - Schema tableValueSchema; - bytes32 table1 = ResourceSelector.from(namespace, name1); - bytes32 table2 = ResourceSelector.from(namespace, name2); - bytes32 table3 = ResourceSelector.from(namespace, name3); - - uint256 value = 1; - bytes32[] key1 = new bytes32[](1); - bytes32[] key2 = new bytes32[](1); - bytes32[] key3 = new bytes32[](1); - bytes32[] key4 = new bytes32[](1); - QueryFragment[] fragmentsHasHas; - QueryFragment[] fragmentsHasNot; + IBaseWorld private world; + KeysInTableModule private keysInTableModule = new KeysInTableModule(); // Modules can be deployed once and installed multiple times + KeysWithValueModule private keysWithValueModule = new KeysWithValueModule(); + + bytes16 private namespace = ROOT_NAMESPACE; + bytes16 private name1 = bytes16("source1"); + bytes16 private name2 = bytes16("source2"); + bytes16 private name3 = bytes16("source3"); + + FieldLayout private tableFieldLayout; + Schema private tableKeySchema; + Schema private tableValueSchema; + bytes32 private table1 = ResourceSelector.from(namespace, name1); + bytes32 private table2 = ResourceSelector.from(namespace, name2); + bytes32 private table3 = ResourceSelector.from(namespace, name3); + + uint256 private value = 1; + bytes32[] private key1 = new bytes32[](1); + bytes32[] private key2 = new bytes32[](1); + bytes32[] private key3 = new bytes32[](1); + bytes32[] private key4 = new bytes32[](1); + QueryFragment[] private fragmentsHasHas; + QueryFragment[] private fragmentsHasNot; function setUp() public { tableFieldLayout = FieldLayoutEncodeHelper.encode(32, 0); @@ -90,9 +91,9 @@ contract QueryTest is Test, GasReporter { function testHasQuery() public { _installKeysInTableModule(); - world.setRecord(table1, key1, abi.encode(1), tableFieldLayout); - world.setRecord(table1, key2, abi.encode(1), tableFieldLayout); - world.setRecord(table2, key1, abi.encode(0), tableFieldLayout); + world.setRecord(table1, key1, abi.encode(1), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); + world.setRecord(table1, key2, abi.encode(1), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); + world.setRecord(table2, key1, abi.encode(0), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); // Query should return all keys in table1 QueryFragment[] memory fragments = new QueryFragment[](1); @@ -111,9 +112,9 @@ contract QueryTest is Test, GasReporter { _installKeysInTableModule(); _installKeysWithValueModule(); - world.setRecord(table1, key1, abi.encode(2), tableFieldLayout); - world.setRecord(table1, key2, abi.encode(1), tableFieldLayout); - world.setRecord(table1, key3, abi.encode(1), tableFieldLayout); + world.setRecord(table1, key1, abi.encode(2), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); + world.setRecord(table1, key2, abi.encode(1), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); + world.setRecord(table1, key3, abi.encode(1), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); // Query should return all keys in table1 with value 1 QueryFragment[] memory fragments = new QueryFragment[](1); fragments[0] = QueryFragment(QueryType.HasValue, table1, abi.encode(1)); @@ -129,12 +130,12 @@ contract QueryTest is Test, GasReporter { function testCombinedHasQuery() public { _installKeysInTableModule(); - world.setRecord(table1, key1, abi.encode(2), tableFieldLayout); - world.setRecord(table1, key2, abi.encode(1), tableFieldLayout); - world.setRecord(table1, key3, abi.encode(1), tableFieldLayout); - world.setRecord(table2, key2, abi.encode(1), tableFieldLayout); - world.setRecord(table2, key3, abi.encode(1), tableFieldLayout); - world.setRecord(table3, key1, abi.encode(1), tableFieldLayout); + world.setRecord(table1, key1, abi.encode(2), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); + world.setRecord(table1, key2, abi.encode(1), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); + world.setRecord(table1, key3, abi.encode(1), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); + world.setRecord(table2, key2, abi.encode(1), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); + world.setRecord(table2, key3, abi.encode(1), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); + world.setRecord(table3, key1, abi.encode(1), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); // Query should return all entities that have table1 and table2 QueryFragment[] memory fragments = new QueryFragment[](2); @@ -153,12 +154,12 @@ contract QueryTest is Test, GasReporter { _installKeysInTableModule(); _installKeysWithValueModule(); - world.setRecord(table1, key1, abi.encode(2), tableFieldLayout); - world.setRecord(table1, key2, abi.encode(2), tableFieldLayout); - world.setRecord(table1, key3, abi.encode(1), tableFieldLayout); - world.setRecord(table2, key2, abi.encode(1), tableFieldLayout); - world.setRecord(table2, key3, abi.encode(1), tableFieldLayout); - world.setRecord(table3, key1, abi.encode(1), tableFieldLayout); + world.setRecord(table1, key1, abi.encode(2), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); + world.setRecord(table1, key2, abi.encode(2), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); + world.setRecord(table1, key3, abi.encode(1), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); + world.setRecord(table2, key2, abi.encode(1), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); + world.setRecord(table2, key3, abi.encode(1), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); + world.setRecord(table3, key1, abi.encode(1), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); // Query should return all entities that have table1 and table2 QueryFragment[] memory fragments = new QueryFragment[](2); @@ -176,13 +177,13 @@ contract QueryTest is Test, GasReporter { _installKeysInTableModule(); _installKeysWithValueModule(); - world.setRecord(table1, key1, abi.encode(1), tableFieldLayout); - world.setRecord(table1, key2, abi.encode(1), tableFieldLayout); - world.setRecord(table1, key3, abi.encode(1), tableFieldLayout); - world.setRecord(table2, key1, abi.encode(1), tableFieldLayout); - world.setRecord(table2, key2, abi.encode(2), tableFieldLayout); - world.setRecord(table2, key3, abi.encode(2), tableFieldLayout); - world.setRecord(table2, key4, abi.encode(2), tableFieldLayout); + world.setRecord(table1, key1, abi.encode(1), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); + world.setRecord(table1, key2, abi.encode(1), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); + world.setRecord(table1, key3, abi.encode(1), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); + world.setRecord(table2, key1, abi.encode(1), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); + world.setRecord(table2, key2, abi.encode(2), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); + world.setRecord(table2, key3, abi.encode(2), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); + world.setRecord(table2, key4, abi.encode(2), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); // Query should return all entities that have table1 and table2 QueryFragment[] memory fragments = new QueryFragment[](2); @@ -200,13 +201,13 @@ contract QueryTest is Test, GasReporter { function testCombinedHasNotQuery() public { _installKeysInTableModule(); - world.setRecord(table1, key1, abi.encode(1), tableFieldLayout); - world.setRecord(table1, key2, abi.encode(1), tableFieldLayout); - world.setRecord(table1, key3, abi.encode(1), tableFieldLayout); - world.setRecord(table2, key1, abi.encode(1), tableFieldLayout); - world.setRecord(table2, key2, abi.encode(2), tableFieldLayout); - world.setRecord(table2, key3, abi.encode(2), tableFieldLayout); - world.setRecord(table2, key4, abi.encode(2), tableFieldLayout); + world.setRecord(table1, key1, abi.encode(1), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); + world.setRecord(table1, key2, abi.encode(1), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); + world.setRecord(table1, key3, abi.encode(1), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); + world.setRecord(table2, key1, abi.encode(1), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); + world.setRecord(table2, key2, abi.encode(2), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); + world.setRecord(table2, key3, abi.encode(2), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); + world.setRecord(table2, key4, abi.encode(2), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); // Query should return all entities that have table1 and table2 QueryFragment[] memory fragments = new QueryFragment[](2); @@ -224,13 +225,13 @@ contract QueryTest is Test, GasReporter { _installKeysInTableModule(); _installKeysWithValueModule(); - world.setRecord(table1, key1, abi.encode(1), tableFieldLayout); - world.setRecord(table1, key2, abi.encode(1), tableFieldLayout); - world.setRecord(table1, key3, abi.encode(1), tableFieldLayout); - world.setRecord(table2, key1, abi.encode(1), tableFieldLayout); - world.setRecord(table2, key2, abi.encode(2), tableFieldLayout); - world.setRecord(table2, key3, abi.encode(1), tableFieldLayout); - world.setRecord(table2, key4, abi.encode(1), tableFieldLayout); + world.setRecord(table1, key1, abi.encode(1), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); + world.setRecord(table1, key2, abi.encode(1), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); + world.setRecord(table1, key3, abi.encode(1), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); + world.setRecord(table2, key1, abi.encode(1), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); + world.setRecord(table2, key2, abi.encode(2), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); + world.setRecord(table2, key3, abi.encode(1), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); + world.setRecord(table2, key4, abi.encode(1), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); // Query should return all entities that have table1 and table2 QueryFragment[] memory fragments = new QueryFragment[](2); @@ -248,16 +249,16 @@ contract QueryTest is Test, GasReporter { _installKeysInTableModule(); _installKeysWithValueModule(); - world.setRecord(table1, key1, abi.encode(1), tableFieldLayout); - world.setRecord(table1, key2, abi.encode(1), tableFieldLayout); - world.setRecord(table1, key3, abi.encode(1), tableFieldLayout); - world.setRecord(table2, key1, abi.encode(1), tableFieldLayout); - world.setRecord(table2, key2, abi.encode(2), tableFieldLayout); - world.setRecord(table2, key3, abi.encode(1), tableFieldLayout); - world.setRecord(table2, key4, abi.encode(1), tableFieldLayout); - world.setRecord(table3, key2, abi.encode(1), tableFieldLayout); - world.setRecord(table3, key3, abi.encode(1), tableFieldLayout); - world.setRecord(table3, key4, abi.encode(1), tableFieldLayout); + world.setRecord(table1, key1, abi.encode(1), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); + world.setRecord(table1, key2, abi.encode(1), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); + world.setRecord(table1, key3, abi.encode(1), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); + world.setRecord(table2, key1, abi.encode(1), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); + world.setRecord(table2, key2, abi.encode(2), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); + world.setRecord(table2, key3, abi.encode(1), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); + world.setRecord(table2, key4, abi.encode(1), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); + world.setRecord(table3, key2, abi.encode(1), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); + world.setRecord(table3, key3, abi.encode(1), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); + world.setRecord(table3, key4, abi.encode(1), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); // Query should return all entities that have table2 and not table1 QueryFragment[] memory fragments = new QueryFragment[](3); @@ -276,9 +277,9 @@ contract QueryTest is Test, GasReporter { _installKeysInTableModule(); _installKeysWithValueModule(); - world.setRecord(table1, key1, abi.encode(4), tableFieldLayout); - world.setRecord(table1, key2, abi.encode(5), tableFieldLayout); - world.setRecord(table1, key3, abi.encode(6), tableFieldLayout); + world.setRecord(table1, key1, abi.encode(4), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); + world.setRecord(table1, key2, abi.encode(5), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); + world.setRecord(table1, key3, abi.encode(6), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); // Query should return all entities with table1 except value 6 QueryFragment[] memory fragments = new QueryFragment[](2); @@ -299,9 +300,9 @@ contract QueryTest is Test, GasReporter { for (uint256 i; i < 100; i++) { bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = bytes32(i); - world.setRecord(table1, keyTuple, abi.encode(1), tableFieldLayout); + world.setRecord(table1, keyTuple, abi.encode(1), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); } - world.setRecord(table2, key1, abi.encode(0), tableFieldLayout); + world.setRecord(table2, key1, abi.encode(0), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); // Query should return all keys in table1 QueryFragment[] memory fragments = new QueryFragment[](1); @@ -320,9 +321,9 @@ contract QueryTest is Test, GasReporter { for (uint256 i; i < 1000; i++) { bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = bytes32(i); - world.setRecord(table1, keyTuple, abi.encode(1), tableFieldLayout); + world.setRecord(table1, keyTuple, abi.encode(1), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); } - world.setRecord(table2, key1, abi.encode(0), tableFieldLayout); + world.setRecord(table2, key1, abi.encode(0), PackedCounter.wrap(bytes32(0)), new bytes(0), tableFieldLayout); // Query should return all keys in table1 QueryFragment[] memory fragments = new QueryFragment[](1); diff --git a/packages/world/test/tables/AddressArray.sol b/packages/world/test/tables/AddressArray.sol index 004e97be40..e2b29648b8 100644 --- a/packages/world/test/tables/AddressArray.sol +++ b/packages/world/test/tables/AddressArray.sol @@ -18,12 +18,14 @@ import { FieldLayout, FieldLayoutLib } from "@latticexyz/store/src/FieldLayout.s import { Schema, SchemaLib } from "@latticexyz/store/src/Schema.sol"; import { PackedCounter, PackedCounterLib } from "@latticexyz/store/src/PackedCounter.sol"; +FieldLayout constant _fieldLayout = FieldLayout.wrap( + 0x0000000100000000000000000000000000000000000000000000000000000000 +); + library AddressArray { /** Get the table values' field layout */ function getFieldLayout() internal pure returns (FieldLayout) { - uint256[] memory _fieldLayout = new uint256[](0); - - return FieldLayoutLib.encode(_fieldLayout, 1); + return _fieldLayout; } /** Get the table's key schema */ @@ -56,19 +58,17 @@ library AddressArray { /** Register the table with its config */ function register(bytes32 _tableId) internal { - StoreSwitch.registerTable( - _tableId, - getFieldLayout(), - getKeySchema(), - getValueSchema(), - getKeyNames(), - getFieldNames() - ); + StoreSwitch.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + } + + /** Register the table with its config */ + function _register(bytes32 _tableId) internal { + StoreCore.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Register the table with its config (using the specified store) */ function register(IStore _store, bytes32 _tableId) internal { - _store.registerTable(_tableId, getFieldLayout(), getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + _store.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Get value */ @@ -76,7 +76,16 @@ library AddressArray { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 0, getFieldLayout()); + bytes memory _blob = StoreSwitch.getDynamicField(_tableId, _keyTuple, 0); + return (SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_address()); + } + + /** Get value */ + function _get(bytes32 _tableId, bytes32 key) internal view returns (address[] memory value) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + bytes memory _blob = StoreCore.getDynamicField(_tableId, _keyTuple, 0); return (SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_address()); } @@ -85,7 +94,7 @@ library AddressArray { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = _store.getField(_tableId, _keyTuple, 0, getFieldLayout()); + bytes memory _blob = _store.getDynamicField(_tableId, _keyTuple, 0); return (SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_address()); } @@ -94,7 +103,15 @@ library AddressArray { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.setField(_tableId, _keyTuple, 0, EncodeArray.encode((value)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 0, EncodeArray.encode((value)), _fieldLayout); + } + + /** Set value */ + function _set(bytes32 _tableId, bytes32 key, address[] memory value) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.setField(_tableId, _keyTuple, 0, EncodeArray.encode((value)), _fieldLayout); } /** Set value (using the specified store) */ @@ -102,7 +119,7 @@ library AddressArray { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.setField(_tableId, _keyTuple, 0, EncodeArray.encode((value)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 0, EncodeArray.encode((value)), _fieldLayout); } /** Get the length of value */ @@ -110,7 +127,18 @@ library AddressArray { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 0, getFieldLayout()); + uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 0, _fieldLayout); + unchecked { + return _byteLength / 20; + } + } + + /** Get the length of value */ + function _length(bytes32 _tableId, bytes32 key) internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + uint256 _byteLength = StoreCore.getFieldLength(_tableId, _keyTuple, 0, _fieldLayout); unchecked { return _byteLength / 20; } @@ -121,7 +149,7 @@ library AddressArray { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 0, getFieldLayout()); + uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 0, _fieldLayout); unchecked { return _byteLength / 20; } @@ -140,32 +168,46 @@ library AddressArray { _tableId, _keyTuple, 0, - getFieldLayout(), + _fieldLayout, _index * 20, (_index + 1) * 20 ); - return (address(Bytes.slice20(_blob, 0))); + return (address(bytes20(_blob))); } } /** - * Get an item of value (using the specified store) + * Get an item of value * (unchecked, returns invalid data if index overflows) */ - function getItem(IStore _store, bytes32 _tableId, bytes32 key, uint256 _index) internal view returns (address) { + function _getItem(bytes32 _tableId, bytes32 key, uint256 _index) internal view returns (address) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; unchecked { - bytes memory _blob = _store.getFieldSlice( + bytes memory _blob = StoreCore.getFieldSlice( _tableId, _keyTuple, 0, - getFieldLayout(), + _fieldLayout, _index * 20, (_index + 1) * 20 ); - return (address(Bytes.slice20(_blob, 0))); + return (address(bytes20(_blob))); + } + } + + /** + * Get an item of value (using the specified store) + * (unchecked, returns invalid data if index overflows) + */ + function getItem(IStore _store, bytes32 _tableId, bytes32 key, uint256 _index) internal view returns (address) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + unchecked { + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 0, _fieldLayout, _index * 20, (_index + 1) * 20); + return (address(bytes20(_blob))); } } @@ -174,7 +216,15 @@ library AddressArray { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), getFieldLayout()); + StoreSwitch.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), _fieldLayout); + } + + /** Push an element to value */ + function _push(bytes32 _tableId, bytes32 key, address _element) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), _fieldLayout); } /** Push an element to value (using the specified store) */ @@ -182,7 +232,7 @@ library AddressArray { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), getFieldLayout()); + _store.pushToField(_tableId, _keyTuple, 0, abi.encodePacked((_element)), _fieldLayout); } /** Pop an element from value */ @@ -190,7 +240,15 @@ library AddressArray { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.popFromField(_tableId, _keyTuple, 0, 20, getFieldLayout()); + StoreSwitch.popFromField(_tableId, _keyTuple, 0, 20, _fieldLayout); + } + + /** Pop an element from value */ + function _pop(bytes32 _tableId, bytes32 key) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.popFromField(_tableId, _keyTuple, 0, 20, _fieldLayout); } /** Pop an element from value (using the specified store) */ @@ -198,7 +256,7 @@ library AddressArray { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.popFromField(_tableId, _keyTuple, 0, 20, getFieldLayout()); + _store.popFromField(_tableId, _keyTuple, 0, 20, _fieldLayout); } /** @@ -210,7 +268,20 @@ library AddressArray { _keyTuple[0] = key; unchecked { - StoreSwitch.updateInField(_tableId, _keyTuple, 0, _index * 20, abi.encodePacked((_element)), getFieldLayout()); + StoreSwitch.updateInField(_tableId, _keyTuple, 0, _index * 20, abi.encodePacked((_element)), _fieldLayout); + } + } + + /** + * Update an element of value at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ + function _update(bytes32 _tableId, bytes32 key, uint256 _index, address _element) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + unchecked { + StoreCore.updateInField(_tableId, _keyTuple, 0, _index * 20, abi.encodePacked((_element)), _fieldLayout); } } @@ -223,19 +294,30 @@ library AddressArray { _keyTuple[0] = key; unchecked { - _store.updateInField(_tableId, _keyTuple, 0, _index * 20, abi.encodePacked((_element)), getFieldLayout()); + _store.updateInField(_tableId, _keyTuple, 0, _index * 20, abi.encodePacked((_element)), _fieldLayout); } } - /** Tightly pack full data using this table's field layout */ - function encode(address[] memory value) internal pure returns (bytes memory) { - PackedCounter _encodedLengths; + /** Tightly pack dynamic data using this table's schema */ + function encodeLengths(address[] memory value) internal pure returns (PackedCounter _encodedLengths) { // Lengths are effectively checked during copy by 2**40 bytes exceeding gas limits unchecked { _encodedLengths = PackedCounterLib.pack(value.length * 20); } + } - return abi.encodePacked(_encodedLengths.unwrap(), EncodeArray.encode((value))); + /** Tightly pack dynamic data using this table's schema */ + function encodeDynamic(address[] memory value) internal pure returns (bytes memory) { + return abi.encodePacked(EncodeArray.encode((value))); + } + + /** Tightly pack full data using this table's field layout */ + function encode(address[] memory value) internal pure returns (bytes memory) { + bytes memory _staticData; + PackedCounter _encodedLengths = encodeLengths(value); + bytes memory _dynamicData = encodeDynamic(value); + + return abi.encodePacked(_staticData, _encodedLengths, _dynamicData); } /** Encode keys as a bytes32 array using this table's field layout */ @@ -251,7 +333,15 @@ library AddressArray { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + StoreSwitch.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /* Delete all data for given keys */ + function _deleteRecord(bytes32 _tableId, bytes32 key) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); } /* Delete all data for given keys (using the specified store) */ @@ -259,6 +349,6 @@ library AddressArray { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + _store.deleteRecord(_tableId, _keyTuple, _fieldLayout); } } diff --git a/packages/world/test/tables/Bool.sol b/packages/world/test/tables/Bool.sol index 955432d6b9..6605ccc924 100644 --- a/packages/world/test/tables/Bool.sol +++ b/packages/world/test/tables/Bool.sol @@ -18,13 +18,14 @@ import { FieldLayout, FieldLayoutLib } from "@latticexyz/store/src/FieldLayout.s import { Schema, SchemaLib } from "@latticexyz/store/src/Schema.sol"; import { PackedCounter, PackedCounterLib } from "@latticexyz/store/src/PackedCounter.sol"; +FieldLayout constant _fieldLayout = FieldLayout.wrap( + 0x0001010001000000000000000000000000000000000000000000000000000000 +); + library Bool { /** Get the table values' field layout */ function getFieldLayout() internal pure returns (FieldLayout) { - uint256[] memory _fieldLayout = new uint256[](1); - _fieldLayout[0] = 1; - - return FieldLayoutLib.encode(_fieldLayout, 0); + return _fieldLayout; } /** Get the table's key schema */ @@ -55,54 +56,77 @@ library Bool { /** Register the table with its config */ function register(bytes32 _tableId) internal { - StoreSwitch.registerTable( - _tableId, - getFieldLayout(), - getKeySchema(), - getValueSchema(), - getKeyNames(), - getFieldNames() - ); + StoreSwitch.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + } + + /** Register the table with its config */ + function _register(bytes32 _tableId) internal { + StoreCore.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Register the table with its config (using the specified store) */ function register(IStore _store, bytes32 _tableId) internal { - _store.registerTable(_tableId, getFieldLayout(), getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + _store.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Get value */ function get(bytes32 _tableId) internal view returns (bool value) { bytes32[] memory _keyTuple = new bytes32[](0); - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (_toBool(uint8(Bytes.slice1(_blob, 0)))); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (_toBool(uint8(bytes1(_blob)))); + } + + /** Get value */ + function _get(bytes32 _tableId) internal view returns (bool value) { + bytes32[] memory _keyTuple = new bytes32[](0); + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (_toBool(uint8(bytes1(_blob)))); } /** Get value (using the specified store) */ function get(IStore _store, bytes32 _tableId) internal view returns (bool value) { bytes32[] memory _keyTuple = new bytes32[](0); - bytes memory _blob = _store.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (_toBool(uint8(Bytes.slice1(_blob, 0)))); + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (_toBool(uint8(bytes1(_blob)))); } /** Set value */ function set(bytes32 _tableId, bool value) internal { bytes32[] memory _keyTuple = new bytes32[](0); - StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((value)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); + } + + /** Set value */ + function _set(bytes32 _tableId, bool value) internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreCore.setField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); } /** Set value (using the specified store) */ function set(IStore _store, bytes32 _tableId, bool value) internal { bytes32[] memory _keyTuple = new bytes32[](0); - _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((value)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); + } + + /** Tightly pack static data using this table's schema */ + function encodeStatic(bool value) internal pure returns (bytes memory) { + return abi.encodePacked(value); } /** Tightly pack full data using this table's field layout */ function encode(bool value) internal pure returns (bytes memory) { - return abi.encodePacked(value); + bytes memory _staticData = encodeStatic(value); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; + + return abi.encodePacked(_staticData, _encodedLengths, _dynamicData); } /** Encode keys as a bytes32 array using this table's field layout */ @@ -116,14 +140,21 @@ library Bool { function deleteRecord(bytes32 _tableId) internal { bytes32[] memory _keyTuple = new bytes32[](0); - StoreSwitch.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + StoreSwitch.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /* Delete all data for given keys */ + function _deleteRecord(bytes32 _tableId) internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); } /* Delete all data for given keys (using the specified store) */ function deleteRecord(IStore _store, bytes32 _tableId) internal { bytes32[] memory _keyTuple = new bytes32[](0); - _store.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + _store.deleteRecord(_tableId, _keyTuple, _fieldLayout); } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b66e40e5bd..b1dec75481 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -405,6 +405,46 @@ importers: packages/ecs-browser: {} + packages/faucet: + dependencies: + '@fastify/cors': + specifier: ^8.3.0 + version: 8.3.0 + '@trpc/client': + specifier: 10.34.0 + version: 10.34.0(@trpc/server@10.34.0) + '@trpc/server': + specifier: 10.34.0 + version: 10.34.0 + debug: + specifier: ^4.3.4 + version: 4.3.4(supports-color@8.1.1) + dotenv: + specifier: ^16.0.3 + version: 16.0.3 + fastify: + specifier: ^4.21.0 + version: 4.21.0 + viem: + specifier: 1.6.0 + version: 1.6.0(typescript@5.1.6)(zod@3.21.4) + zod: + specifier: ^3.21.4 + version: 3.21.4 + devDependencies: + '@types/debug': + specifier: ^4.1.7 + version: 4.1.7 + tsup: + specifier: ^6.7.0 + version: 6.7.0(postcss@8.4.23)(typescript@5.1.6) + tsx: + specifier: ^3.12.6 + version: 3.12.6 + vitest: + specifier: 0.31.4 + version: 0.31.4(jsdom@22.1.0) + packages/gas-report: dependencies: chalk: @@ -782,14 +822,14 @@ importers: specifier: ^0.2.22 version: 0.2.22(typescript@5.1.6) better-sqlite3: - specifier: ^8.4.0 - version: 8.4.0 + specifier: ^8.6.0 + version: 8.6.0 debug: specifier: ^4.3.4 version: 4.3.4(supports-color@8.1.1) drizzle-orm: - specifier: ^0.27.0 - version: 0.27.0(@types/better-sqlite3@7.6.4)(better-sqlite3@8.4.0)(postgres@3.3.5) + specifier: ^0.28.5 + version: 0.28.5(@types/better-sqlite3@7.6.4)(better-sqlite3@8.6.0)(postgres@3.3.5) fastify: specifier: ^4.21.0 version: 4.21.0 @@ -861,11 +901,11 @@ importers: specifier: ^4.3.4 version: 4.3.4(supports-color@8.1.1) drizzle-orm: - specifier: ^0.27.0 - version: 0.27.0(@types/sql.js@1.4.4)(kysely@0.26.1)(postgres@3.3.5)(sql.js@1.8.0) + specifier: ^0.28.5 + version: 0.28.5(@types/sql.js@1.4.4)(kysely@0.26.3)(postgres@3.3.5)(sql.js@1.8.0) kysely: - specifier: ^0.26.1 - version: 0.26.1 + specifier: ^0.26.3 + version: 0.26.3 postgres: specifier: ^3.3.5 version: 3.3.5 @@ -4064,8 +4104,8 @@ packages: is-windows: 1.0.2 dev: true - /better-sqlite3@8.4.0: - resolution: {integrity: sha512-NmsNW1CQvqMszu/CFAJ3pLct6NEFlNfuGM6vw72KHkjOD1UDnL96XNN1BMQc1hiHo8vE2GbOWQYIpZ+YM5wrZw==} + /better-sqlite3@8.6.0: + resolution: {integrity: sha512-jwAudeiTMTSyby+/SfbHDebShbmC2MCH8mU2+DXi0WJfv13ypEJm47cd3kljmy/H130CazEvkf2Li//ewcMJ1g==} requiresBuild: true dependencies: bindings: 1.5.0 @@ -4953,8 +4993,8 @@ packages: detect-libc: 1.0.3 dev: true - /drizzle-orm@0.27.0(@types/better-sqlite3@7.6.4)(better-sqlite3@8.4.0)(postgres@3.3.5): - resolution: {integrity: sha512-LGiJ0icB+wQwgbSCOvAjONY8Ec6G/EDzQQP5PmUaQYeI9OqgpVKHC2T1fFIbvk5dabWsbokJ5NOciVAxriStig==} + /drizzle-orm@0.28.5(@types/better-sqlite3@7.6.4)(better-sqlite3@8.6.0)(postgres@3.3.5): + resolution: {integrity: sha512-6r6Iw4c38NAmW6TiKH3TUpGUQ1YdlEoLJOQptn8XPx3Z63+vFNKfAiANqrIiYZiMjKR9+NYAL219nFrmo1duXA==} peerDependencies: '@aws-sdk/client-rds-data': '>=3' '@cloudflare/workers-types': '>=3' @@ -5016,12 +5056,12 @@ packages: optional: true dependencies: '@types/better-sqlite3': 7.6.4 - better-sqlite3: 8.4.0 + better-sqlite3: 8.6.0 postgres: 3.3.5 dev: false - /drizzle-orm@0.27.0(@types/sql.js@1.4.4)(kysely@0.26.1)(postgres@3.3.5)(sql.js@1.8.0): - resolution: {integrity: sha512-LGiJ0icB+wQwgbSCOvAjONY8Ec6G/EDzQQP5PmUaQYeI9OqgpVKHC2T1fFIbvk5dabWsbokJ5NOciVAxriStig==} + /drizzle-orm@0.28.5(@types/sql.js@1.4.4)(kysely@0.26.3)(postgres@3.3.5)(sql.js@1.8.0): + resolution: {integrity: sha512-6r6Iw4c38NAmW6TiKH3TUpGUQ1YdlEoLJOQptn8XPx3Z63+vFNKfAiANqrIiYZiMjKR9+NYAL219nFrmo1duXA==} peerDependencies: '@aws-sdk/client-rds-data': '>=3' '@cloudflare/workers-types': '>=3' @@ -5083,7 +5123,7 @@ packages: optional: true dependencies: '@types/sql.js': 1.4.4 - kysely: 0.26.1 + kysely: 0.26.3 postgres: 3.3.5 sql.js: 1.8.0 dev: false @@ -7630,8 +7670,8 @@ packages: engines: {node: '>=6'} dev: true - /kysely@0.26.1: - resolution: {integrity: sha512-FVRomkdZofBu3O8SiwAOXrwbhPZZr8mBN5ZeUWyprH29jzvy6Inzqbd0IMmGxpd4rcOCL9HyyBNWBa8FBqDAdg==} + /kysely@0.26.3: + resolution: {integrity: sha512-yWSgGi9bY13b/W06DD2OCDDHQmq1kwTGYlQ4wpZkMOJqMGCstVCFIvxCCVG4KfY1/3G0MhDAcZsip/Lw8/vJWw==} engines: {node: '>=14.0.0'} dev: false diff --git a/templates/phaser/packages/client/src/mud/setupNetwork.ts b/templates/phaser/packages/client/src/mud/setupNetwork.ts index 66a27a95ae..6fe7d00e2c 100644 --- a/templates/phaser/packages/client/src/mud/setupNetwork.ts +++ b/templates/phaser/packages/client/src/mud/setupNetwork.ts @@ -72,7 +72,7 @@ export async function setupNetwork() { * to the viem publicClient to make RPC calls to fetch MUD * events from the chain. */ - const { components, latestBlock$, blockStorageOperations$, waitForTransaction } = await syncToRecs({ + const { components, latestBlock$, storedBlockLogs$, waitForTransaction } = await syncToRecs({ world, config: mudConfig, address: networkConfig.worldAddress as Hex, @@ -115,7 +115,7 @@ export async function setupNetwork() { publicClient, walletClient: burnerWalletClient, latestBlock$, - blockStorageOperations$, + storedBlockLogs$, waitForTransaction, worldContract, write$: write$.asObservable().pipe(share()), diff --git a/templates/phaser/packages/client/src/ui/App.tsx b/templates/phaser/packages/client/src/ui/App.tsx index 47c66ae8f8..a61f2ba87e 100644 --- a/templates/phaser/packages/client/src/ui/App.tsx +++ b/templates/phaser/packages/client/src/ui/App.tsx @@ -21,7 +21,7 @@ export const App = () => { publicClient: networkLayer.network.publicClient, walletClient: networkLayer.network.walletClient, latestBlock$: networkLayer.network.latestBlock$, - blockStorageOperations$: networkLayer.network.blockStorageOperations$, + storedBlockLogs$: networkLayer.network.storedBlockLogs$, worldAddress: networkLayer.network.worldContract.address, worldAbi: networkLayer.network.worldContract.abi, write$: networkLayer.network.write$, diff --git a/templates/phaser/packages/contracts/src/codegen/tables/Counter.sol b/templates/phaser/packages/contracts/src/codegen/tables/Counter.sol index 302b905c36..24ba4c5d0f 100644 --- a/templates/phaser/packages/contracts/src/codegen/tables/Counter.sol +++ b/templates/phaser/packages/contracts/src/codegen/tables/Counter.sol @@ -21,13 +21,14 @@ import { PackedCounter, PackedCounterLib } from "@latticexyz/store/src/PackedCou bytes32 constant _tableId = bytes32(abi.encodePacked(bytes16(""), bytes16("Counter"))); bytes32 constant CounterTableId = _tableId; +FieldLayout constant _fieldLayout = FieldLayout.wrap( + 0x0004010004000000000000000000000000000000000000000000000000000000 +); + library Counter { /** Get the table values' field layout */ function getFieldLayout() internal pure returns (FieldLayout) { - uint256[] memory _fieldLayout = new uint256[](1); - _fieldLayout[0] = 4; - - return FieldLayoutLib.encode(_fieldLayout, 0); + return _fieldLayout; } /** Get the table's key schema */ @@ -58,54 +59,77 @@ library Counter { /** Register the table with its config */ function register() internal { - StoreSwitch.registerTable( - _tableId, - getFieldLayout(), - getKeySchema(), - getValueSchema(), - getKeyNames(), - getFieldNames() - ); + StoreSwitch.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + } + + /** Register the table with its config */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Register the table with its config (using the specified store) */ function register(IStore _store) internal { - _store.registerTable(_tableId, getFieldLayout(), getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + _store.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Get value */ function get() internal view returns (uint32 value) { bytes32[] memory _keyTuple = new bytes32[](0); - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (uint32(Bytes.slice4(_blob, 0))); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint32(bytes4(_blob))); + } + + /** Get value */ + function _get() internal view returns (uint32 value) { + bytes32[] memory _keyTuple = new bytes32[](0); + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint32(bytes4(_blob))); } /** Get value (using the specified store) */ function get(IStore _store) internal view returns (uint32 value) { bytes32[] memory _keyTuple = new bytes32[](0); - bytes memory _blob = _store.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (uint32(Bytes.slice4(_blob, 0))); + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint32(bytes4(_blob))); } /** Set value */ function set(uint32 value) internal { bytes32[] memory _keyTuple = new bytes32[](0); - StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((value)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); + } + + /** Set value */ + function _set(uint32 value) internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreCore.setField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); } /** Set value (using the specified store) */ function set(IStore _store, uint32 value) internal { bytes32[] memory _keyTuple = new bytes32[](0); - _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((value)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); + } + + /** Tightly pack static data using this table's schema */ + function encodeStatic(uint32 value) internal pure returns (bytes memory) { + return abi.encodePacked(value); } /** Tightly pack full data using this table's field layout */ function encode(uint32 value) internal pure returns (bytes memory) { - return abi.encodePacked(value); + bytes memory _staticData = encodeStatic(value); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; + + return abi.encodePacked(_staticData, _encodedLengths, _dynamicData); } /** Encode keys as a bytes32 array using this table's field layout */ @@ -119,13 +143,20 @@ library Counter { function deleteRecord() internal { bytes32[] memory _keyTuple = new bytes32[](0); - StoreSwitch.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + StoreSwitch.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /* Delete all data for given keys */ + function _deleteRecord() internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); } /* Delete all data for given keys (using the specified store) */ function deleteRecord(IStore _store) internal { bytes32[] memory _keyTuple = new bytes32[](0); - _store.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + _store.deleteRecord(_tableId, _keyTuple, _fieldLayout); } } diff --git a/templates/react/packages/client/src/index.tsx b/templates/react/packages/client/src/index.tsx index 9b3f9121e1..da8d70f020 100644 --- a/templates/react/packages/client/src/index.tsx +++ b/templates/react/packages/client/src/index.tsx @@ -24,7 +24,7 @@ setup().then(async (result) => { publicClient: result.network.publicClient, walletClient: result.network.walletClient, latestBlock$: result.network.latestBlock$, - blockStorageOperations$: result.network.blockStorageOperations$, + storedBlockLogs$: result.network.storedBlockLogs$, worldAddress: result.network.worldContract.address, worldAbi: result.network.worldContract.abi, write$: result.network.write$, diff --git a/templates/react/packages/client/src/mud/setupNetwork.ts b/templates/react/packages/client/src/mud/setupNetwork.ts index de4b2c23dd..15d3555441 100644 --- a/templates/react/packages/client/src/mud/setupNetwork.ts +++ b/templates/react/packages/client/src/mud/setupNetwork.ts @@ -74,7 +74,7 @@ export async function setupNetwork() { * to the viem publicClient to make RPC calls to fetch MUD * events from the chain. */ - const { components, latestBlock$, blockStorageOperations$, waitForTransaction } = await syncToRecs({ + const { components, latestBlock$, storedBlockLogs$, waitForTransaction } = await syncToRecs({ world, config: mudConfig, address: networkConfig.worldAddress as Hex, @@ -117,7 +117,7 @@ export async function setupNetwork() { publicClient, walletClient: burnerWalletClient, latestBlock$, - blockStorageOperations$, + storedBlockLogs$, waitForTransaction, worldContract, write$: write$.asObservable().pipe(share()), diff --git a/templates/react/packages/contracts/src/codegen/tables/Counter.sol b/templates/react/packages/contracts/src/codegen/tables/Counter.sol index 302b905c36..24ba4c5d0f 100644 --- a/templates/react/packages/contracts/src/codegen/tables/Counter.sol +++ b/templates/react/packages/contracts/src/codegen/tables/Counter.sol @@ -21,13 +21,14 @@ import { PackedCounter, PackedCounterLib } from "@latticexyz/store/src/PackedCou bytes32 constant _tableId = bytes32(abi.encodePacked(bytes16(""), bytes16("Counter"))); bytes32 constant CounterTableId = _tableId; +FieldLayout constant _fieldLayout = FieldLayout.wrap( + 0x0004010004000000000000000000000000000000000000000000000000000000 +); + library Counter { /** Get the table values' field layout */ function getFieldLayout() internal pure returns (FieldLayout) { - uint256[] memory _fieldLayout = new uint256[](1); - _fieldLayout[0] = 4; - - return FieldLayoutLib.encode(_fieldLayout, 0); + return _fieldLayout; } /** Get the table's key schema */ @@ -58,54 +59,77 @@ library Counter { /** Register the table with its config */ function register() internal { - StoreSwitch.registerTable( - _tableId, - getFieldLayout(), - getKeySchema(), - getValueSchema(), - getKeyNames(), - getFieldNames() - ); + StoreSwitch.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + } + + /** Register the table with its config */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Register the table with its config (using the specified store) */ function register(IStore _store) internal { - _store.registerTable(_tableId, getFieldLayout(), getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + _store.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Get value */ function get() internal view returns (uint32 value) { bytes32[] memory _keyTuple = new bytes32[](0); - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (uint32(Bytes.slice4(_blob, 0))); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint32(bytes4(_blob))); + } + + /** Get value */ + function _get() internal view returns (uint32 value) { + bytes32[] memory _keyTuple = new bytes32[](0); + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint32(bytes4(_blob))); } /** Get value (using the specified store) */ function get(IStore _store) internal view returns (uint32 value) { bytes32[] memory _keyTuple = new bytes32[](0); - bytes memory _blob = _store.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (uint32(Bytes.slice4(_blob, 0))); + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint32(bytes4(_blob))); } /** Set value */ function set(uint32 value) internal { bytes32[] memory _keyTuple = new bytes32[](0); - StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((value)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); + } + + /** Set value */ + function _set(uint32 value) internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreCore.setField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); } /** Set value (using the specified store) */ function set(IStore _store, uint32 value) internal { bytes32[] memory _keyTuple = new bytes32[](0); - _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((value)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); + } + + /** Tightly pack static data using this table's schema */ + function encodeStatic(uint32 value) internal pure returns (bytes memory) { + return abi.encodePacked(value); } /** Tightly pack full data using this table's field layout */ function encode(uint32 value) internal pure returns (bytes memory) { - return abi.encodePacked(value); + bytes memory _staticData = encodeStatic(value); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; + + return abi.encodePacked(_staticData, _encodedLengths, _dynamicData); } /** Encode keys as a bytes32 array using this table's field layout */ @@ -119,13 +143,20 @@ library Counter { function deleteRecord() internal { bytes32[] memory _keyTuple = new bytes32[](0); - StoreSwitch.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + StoreSwitch.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /* Delete all data for given keys */ + function _deleteRecord() internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); } /* Delete all data for given keys (using the specified store) */ function deleteRecord(IStore _store) internal { bytes32[] memory _keyTuple = new bytes32[](0); - _store.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + _store.deleteRecord(_tableId, _keyTuple, _fieldLayout); } } diff --git a/templates/threejs/packages/client/src/index.tsx b/templates/threejs/packages/client/src/index.tsx index 9b3f9121e1..da8d70f020 100644 --- a/templates/threejs/packages/client/src/index.tsx +++ b/templates/threejs/packages/client/src/index.tsx @@ -24,7 +24,7 @@ setup().then(async (result) => { publicClient: result.network.publicClient, walletClient: result.network.walletClient, latestBlock$: result.network.latestBlock$, - blockStorageOperations$: result.network.blockStorageOperations$, + storedBlockLogs$: result.network.storedBlockLogs$, worldAddress: result.network.worldContract.address, worldAbi: result.network.worldContract.abi, write$: result.network.write$, diff --git a/templates/threejs/packages/client/src/mud/setupNetwork.ts b/templates/threejs/packages/client/src/mud/setupNetwork.ts index ca2dc8d669..eb8a98ae84 100644 --- a/templates/threejs/packages/client/src/mud/setupNetwork.ts +++ b/templates/threejs/packages/client/src/mud/setupNetwork.ts @@ -72,7 +72,7 @@ export async function setupNetwork() { * to the viem publicClient to make RPC calls to fetch MUD * events from the chain. */ - const { components, latestBlock$, blockStorageOperations$, waitForTransaction } = await syncToRecs({ + const { components, latestBlock$, storedBlockLogs$, waitForTransaction } = await syncToRecs({ world, config: mudConfig, address: networkConfig.worldAddress as Hex, @@ -115,7 +115,7 @@ export async function setupNetwork() { publicClient, walletClient: burnerWalletClient, latestBlock$, - blockStorageOperations$, + storedBlockLogs$, waitForTransaction, worldContract, write$: write$.asObservable().pipe(share()), diff --git a/templates/threejs/packages/contracts/src/codegen/tables/Position.sol b/templates/threejs/packages/contracts/src/codegen/tables/Position.sol index fa15b967dc..1ea72f6f0b 100644 --- a/templates/threejs/packages/contracts/src/codegen/tables/Position.sol +++ b/templates/threejs/packages/contracts/src/codegen/tables/Position.sol @@ -21,6 +21,10 @@ import { PackedCounter, PackedCounterLib } from "@latticexyz/store/src/PackedCou bytes32 constant _tableId = bytes32(abi.encodePacked(bytes16(""), bytes16("Position"))); bytes32 constant PositionTableId = _tableId; +FieldLayout constant _fieldLayout = FieldLayout.wrap( + 0x000c030004040400000000000000000000000000000000000000000000000000 +); + struct PositionData { int32 x; int32 y; @@ -30,12 +34,7 @@ struct PositionData { library Position { /** Get the table values' field layout */ function getFieldLayout() internal pure returns (FieldLayout) { - uint256[] memory _fieldLayout = new uint256[](3); - _fieldLayout[0] = 4; - _fieldLayout[1] = 4; - _fieldLayout[2] = 4; - - return FieldLayoutLib.encode(_fieldLayout, 0); + return _fieldLayout; } /** Get the table's key schema */ @@ -72,19 +71,17 @@ library Position { /** Register the table with its config */ function register() internal { - StoreSwitch.registerTable( - _tableId, - getFieldLayout(), - getKeySchema(), - getValueSchema(), - getKeyNames(), - getFieldNames() - ); + StoreSwitch.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + } + + /** Register the table with its config */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Register the table with its config (using the specified store) */ function register(IStore _store) internal { - _store.registerTable(_tableId, getFieldLayout(), getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + _store.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Get x */ @@ -92,8 +89,17 @@ library Position { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (int32(uint32(Bytes.slice4(_blob, 0)))); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (int32(uint32(bytes4(_blob)))); + } + + /** Get x */ + function _getX(bytes32 key) internal view returns (int32 x) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (int32(uint32(bytes4(_blob)))); } /** Get x (using the specified store) */ @@ -101,8 +107,8 @@ library Position { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = _store.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (int32(uint32(Bytes.slice4(_blob, 0)))); + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (int32(uint32(bytes4(_blob)))); } /** Set x */ @@ -110,7 +116,15 @@ library Position { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((x)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((x)), _fieldLayout); + } + + /** Set x */ + function _setX(bytes32 key, int32 x) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.setField(_tableId, _keyTuple, 0, abi.encodePacked((x)), _fieldLayout); } /** Set x (using the specified store) */ @@ -118,7 +132,7 @@ library Position { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((x)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((x)), _fieldLayout); } /** Get y */ @@ -126,8 +140,17 @@ library Position { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 1, getFieldLayout()); - return (int32(uint32(Bytes.slice4(_blob, 0)))); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 1, _fieldLayout); + return (int32(uint32(bytes4(_blob)))); + } + + /** Get y */ + function _getY(bytes32 key) internal view returns (int32 y) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 1, _fieldLayout); + return (int32(uint32(bytes4(_blob)))); } /** Get y (using the specified store) */ @@ -135,8 +158,8 @@ library Position { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = _store.getField(_tableId, _keyTuple, 1, getFieldLayout()); - return (int32(uint32(Bytes.slice4(_blob, 0)))); + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 1, _fieldLayout); + return (int32(uint32(bytes4(_blob)))); } /** Set y */ @@ -144,7 +167,15 @@ library Position { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.setField(_tableId, _keyTuple, 1, abi.encodePacked((y)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 1, abi.encodePacked((y)), _fieldLayout); + } + + /** Set y */ + function _setY(bytes32 key, int32 y) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.setField(_tableId, _keyTuple, 1, abi.encodePacked((y)), _fieldLayout); } /** Set y (using the specified store) */ @@ -152,7 +183,7 @@ library Position { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.setField(_tableId, _keyTuple, 1, abi.encodePacked((y)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 1, abi.encodePacked((y)), _fieldLayout); } /** Get z */ @@ -160,8 +191,17 @@ library Position { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 2, getFieldLayout()); - return (int32(uint32(Bytes.slice4(_blob, 0)))); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 2, _fieldLayout); + return (int32(uint32(bytes4(_blob)))); + } + + /** Get z */ + function _getZ(bytes32 key) internal view returns (int32 z) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 2, _fieldLayout); + return (int32(uint32(bytes4(_blob)))); } /** Get z (using the specified store) */ @@ -169,8 +209,8 @@ library Position { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = _store.getField(_tableId, _keyTuple, 2, getFieldLayout()); - return (int32(uint32(Bytes.slice4(_blob, 0)))); + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 2, _fieldLayout); + return (int32(uint32(bytes4(_blob)))); } /** Set z */ @@ -178,7 +218,15 @@ library Position { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.setField(_tableId, _keyTuple, 2, abi.encodePacked((z)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 2, abi.encodePacked((z)), _fieldLayout); + } + + /** Set z */ + function _setZ(bytes32 key, int32 z) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.setField(_tableId, _keyTuple, 2, abi.encodePacked((z)), _fieldLayout); } /** Set z (using the specified store) */ @@ -186,7 +234,7 @@ library Position { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.setField(_tableId, _keyTuple, 2, abi.encodePacked((z)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 2, abi.encodePacked((z)), _fieldLayout); } /** Get the full data */ @@ -194,7 +242,16 @@ library Position { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = StoreSwitch.getRecord(_tableId, _keyTuple, getFieldLayout()); + bytes memory _blob = StoreSwitch.getRecord(_tableId, _keyTuple, _fieldLayout); + return decode(_blob); + } + + /** Get the full data */ + function _get(bytes32 key) internal view returns (PositionData memory _table) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + bytes memory _blob = StoreCore.getRecord(_tableId, _keyTuple, _fieldLayout); return decode(_blob); } @@ -203,28 +260,47 @@ library Position { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = _store.getRecord(_tableId, _keyTuple, getFieldLayout()); + bytes memory _blob = _store.getRecord(_tableId, _keyTuple, _fieldLayout); return decode(_blob); } /** Set the full data using individual values */ function set(bytes32 key, int32 x, int32 y, int32 z) internal { - bytes memory _data = encode(x, y, z); + bytes memory _staticData = encodeStatic(x, y, z); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.setRecord(_tableId, _keyTuple, _data, getFieldLayout()); + StoreSwitch.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); + } + + /** Set the full data using individual values */ + function _set(bytes32 key, int32 x, int32 y, int32 z) internal { + bytes memory _staticData = encodeStatic(x, y, z); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; + + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); } /** Set the full data using individual values (using the specified store) */ function set(IStore _store, bytes32 key, int32 x, int32 y, int32 z) internal { - bytes memory _data = encode(x, y, z); + bytes memory _staticData = encodeStatic(x, y, z); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.setRecord(_tableId, _keyTuple, _data, getFieldLayout()); + _store.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); } /** Set the full data using the data struct */ @@ -232,6 +308,11 @@ library Position { set(key, _table.x, _table.y, _table.z); } + /** Set the full data using the data struct */ + function _set(bytes32 key, PositionData memory _table) internal { + set(key, _table.x, _table.y, _table.z); + } + /** Set the full data using the data struct (using the specified store) */ function set(IStore _store, bytes32 key, PositionData memory _table) internal { set(_store, key, _table.x, _table.y, _table.z); @@ -246,9 +327,19 @@ library Position { _table.z = (int32(uint32(Bytes.slice4(_blob, 8)))); } + /** Tightly pack static data using this table's schema */ + function encodeStatic(int32 x, int32 y, int32 z) internal pure returns (bytes memory) { + return abi.encodePacked(x, y, z); + } + /** Tightly pack full data using this table's field layout */ function encode(int32 x, int32 y, int32 z) internal pure returns (bytes memory) { - return abi.encodePacked(x, y, z); + bytes memory _staticData = encodeStatic(x, y, z); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; + + return abi.encodePacked(_staticData, _encodedLengths, _dynamicData); } /** Encode keys as a bytes32 array using this table's field layout */ @@ -264,7 +355,15 @@ library Position { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + StoreSwitch.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /* Delete all data for given keys */ + function _deleteRecord(bytes32 key) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = key; + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); } /* Delete all data for given keys (using the specified store) */ @@ -272,6 +371,6 @@ library Position { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + _store.deleteRecord(_tableId, _keyTuple, _fieldLayout); } } diff --git a/templates/vanilla/packages/client/src/index.ts b/templates/vanilla/packages/client/src/index.ts index 71b05b1f8d..0c7dda5720 100644 --- a/templates/vanilla/packages/client/src/index.ts +++ b/templates/vanilla/packages/client/src/index.ts @@ -28,7 +28,7 @@ if (import.meta.env.DEV) { publicClient: network.publicClient, walletClient: network.walletClient, latestBlock$: network.latestBlock$, - blockStorageOperations$: network.blockStorageOperations$, + storedBlockLogs$: network.storedBlockLogs$, worldAddress: network.worldContract.address, worldAbi: network.worldContract.abi, write$: network.write$, diff --git a/templates/vanilla/packages/client/src/mud/setupNetwork.ts b/templates/vanilla/packages/client/src/mud/setupNetwork.ts index de4b2c23dd..15d3555441 100644 --- a/templates/vanilla/packages/client/src/mud/setupNetwork.ts +++ b/templates/vanilla/packages/client/src/mud/setupNetwork.ts @@ -74,7 +74,7 @@ export async function setupNetwork() { * to the viem publicClient to make RPC calls to fetch MUD * events from the chain. */ - const { components, latestBlock$, blockStorageOperations$, waitForTransaction } = await syncToRecs({ + const { components, latestBlock$, storedBlockLogs$, waitForTransaction } = await syncToRecs({ world, config: mudConfig, address: networkConfig.worldAddress as Hex, @@ -117,7 +117,7 @@ export async function setupNetwork() { publicClient, walletClient: burnerWalletClient, latestBlock$, - blockStorageOperations$, + storedBlockLogs$, waitForTransaction, worldContract, write$: write$.asObservable().pipe(share()), diff --git a/templates/vanilla/packages/contracts/src/codegen/tables/Counter.sol b/templates/vanilla/packages/contracts/src/codegen/tables/Counter.sol index 302b905c36..24ba4c5d0f 100644 --- a/templates/vanilla/packages/contracts/src/codegen/tables/Counter.sol +++ b/templates/vanilla/packages/contracts/src/codegen/tables/Counter.sol @@ -21,13 +21,14 @@ import { PackedCounter, PackedCounterLib } from "@latticexyz/store/src/PackedCou bytes32 constant _tableId = bytes32(abi.encodePacked(bytes16(""), bytes16("Counter"))); bytes32 constant CounterTableId = _tableId; +FieldLayout constant _fieldLayout = FieldLayout.wrap( + 0x0004010004000000000000000000000000000000000000000000000000000000 +); + library Counter { /** Get the table values' field layout */ function getFieldLayout() internal pure returns (FieldLayout) { - uint256[] memory _fieldLayout = new uint256[](1); - _fieldLayout[0] = 4; - - return FieldLayoutLib.encode(_fieldLayout, 0); + return _fieldLayout; } /** Get the table's key schema */ @@ -58,54 +59,77 @@ library Counter { /** Register the table with its config */ function register() internal { - StoreSwitch.registerTable( - _tableId, - getFieldLayout(), - getKeySchema(), - getValueSchema(), - getKeyNames(), - getFieldNames() - ); + StoreSwitch.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + } + + /** Register the table with its config */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Register the table with its config (using the specified store) */ function register(IStore _store) internal { - _store.registerTable(_tableId, getFieldLayout(), getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + _store.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); } /** Get value */ function get() internal view returns (uint32 value) { bytes32[] memory _keyTuple = new bytes32[](0); - bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (uint32(Bytes.slice4(_blob, 0))); + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint32(bytes4(_blob))); + } + + /** Get value */ + function _get() internal view returns (uint32 value) { + bytes32[] memory _keyTuple = new bytes32[](0); + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint32(bytes4(_blob))); } /** Get value (using the specified store) */ function get(IStore _store) internal view returns (uint32 value) { bytes32[] memory _keyTuple = new bytes32[](0); - bytes memory _blob = _store.getField(_tableId, _keyTuple, 0, getFieldLayout()); - return (uint32(Bytes.slice4(_blob, 0))); + bytes32 _blob = _store.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint32(bytes4(_blob))); } /** Set value */ function set(uint32 value) internal { bytes32[] memory _keyTuple = new bytes32[](0); - StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((value)), getFieldLayout()); + StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); + } + + /** Set value */ + function _set(uint32 value) internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreCore.setField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); } /** Set value (using the specified store) */ function set(IStore _store, uint32 value) internal { bytes32[] memory _keyTuple = new bytes32[](0); - _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((value)), getFieldLayout()); + _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); + } + + /** Tightly pack static data using this table's schema */ + function encodeStatic(uint32 value) internal pure returns (bytes memory) { + return abi.encodePacked(value); } /** Tightly pack full data using this table's field layout */ function encode(uint32 value) internal pure returns (bytes memory) { - return abi.encodePacked(value); + bytes memory _staticData = encodeStatic(value); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; + + return abi.encodePacked(_staticData, _encodedLengths, _dynamicData); } /** Encode keys as a bytes32 array using this table's field layout */ @@ -119,13 +143,20 @@ library Counter { function deleteRecord() internal { bytes32[] memory _keyTuple = new bytes32[](0); - StoreSwitch.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + StoreSwitch.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /* Delete all data for given keys */ + function _deleteRecord() internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); } /* Delete all data for given keys (using the specified store) */ function deleteRecord(IStore _store) internal { bytes32[] memory _keyTuple = new bytes32[](0); - _store.deleteRecord(_tableId, _keyTuple, getFieldLayout()); + _store.deleteRecord(_tableId, _keyTuple, _fieldLayout); } } diff --git a/test-data/world-logs.json b/test-data/world-logs.json index 4faea58887..01c17d0650 100644 --- a/test-data/world-logs.json +++ b/test-data/world-logs.json @@ -2,824 +2,883 @@ { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0x912af873e852235aae78a1d25ae9bb28b616a67c36898c53a14fd8184504ee32" + "0xc851f86da4b2f7d69c86fc8dc136b05aa9805e49c225918a8856af392597a34f", + "0x6d756473746f726500000000000000005461626c657300000000000000000000" ], - "data": "0x6d756473746f726500000000000000005461626c657300000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000016d756473746f726500000000000000005461626c65730000000000000000000000000000000000000000000000000000000000000000000000000000000003400060030220202000000000000000000000000000000000000000000000000000002001005f000000000000000000000000000000000000000000000000000000006003025f5f5fc4c40000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000000a0000000000002c000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000077461626c654964000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000000b6669656c644c61796f757400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000096b6579536368656d610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b76616c7565536368656d610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012616269456e636f6465644b65794e616d657300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014616269456e636f6465644669656c644e616d6573000000000000000000000000", - "blockHash": "0xfe43f23153c69149ebe20b5941cccf8c55f4bf3acd82b33445ace71a2c5f8a9c", - "blockNumber": "0x2", - "transactionHash": "0xae82b15a8c62db1e23d9c523e730d9b861eed20529b8cff4d3452f86ae3149df", - "transactionIndex": "0x7", - "logIndex": "0x1", + "data": "0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000022000000000a0000000000002c0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000016d756473746f726500000000000000005461626c65730000000000000000000000000000000000000000000000000000000000000000000000000000000000600060030220202000000000000000000000000000000000000000000000000000002001005f000000000000000000000000000000000000000000000000000000006003025f5f5fc4c4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000077461626c654964000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000000b6669656c644c61796f757400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000096b6579536368656d610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b76616c7565536368656d610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012616269456e636f6465644b65794e616d657300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014616269456e636f6465644669656c644e616d6573000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0xa4f8502ae902c2b982a75bee37ad2c52b7456d38cb4fdd2288dbda0d53b5db23", + "transactionIndex": "0x0", + "logIndex": "0x0", "transactionLogIndex": "0x0", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0x912af873e852235aae78a1d25ae9bb28b616a67c36898c53a14fd8184504ee32" + "0xc851f86da4b2f7d69c86fc8dc136b05aa9805e49c225918a8856af392597a34f", + "0x6d756473746f726500000000000000005461626c657300000000000000000000" ], - "data": "0x6d756473746f726500000000000000005461626c657300000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000016d756473746f7265000000000000000053746f7265486f6f6b7300000000000000000000000000000000000000000000000000000000000000000000000001c00000000100000000000000000000000000000000000000000000000000000000002001005f00000000000000000000000000000000000000000000000000000000000001b600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000a00000000000014000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000036b65790000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000576616c7565000000000000000000000000000000000000000000000000000000", - "blockHash": "0xfe43f23153c69149ebe20b5941cccf8c55f4bf3acd82b33445ace71a2c5f8a9c", - "blockNumber": "0x2", - "transactionHash": "0xae82b15a8c62db1e23d9c523e730d9b861eed20529b8cff4d3452f86ae3149df", - "transactionIndex": "0x7", - "logIndex": "0x2", + "data": "0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000a000000000a000000000000140000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000016d756473746f7265000000000000000053746f7265486f6f6b7300000000000000000000000000000000000000000000000000000000000000000000000000600000000100000000000000000000000000000000000000000000000000000000002001005f00000000000000000000000000000000000000000000000000000000000001b6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000036b65790000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000576616c7565000000000000000000000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0xa4f8502ae902c2b982a75bee37ad2c52b7456d38cb4fdd2288dbda0d53b5db23", + "transactionIndex": "0x0", + "logIndex": "0x1", "transactionLogIndex": "0x1", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0x912af873e852235aae78a1d25ae9bb28b616a67c36898c53a14fd8184504ee32" + "0xc851f86da4b2f7d69c86fc8dc136b05aa9805e49c225918a8856af392597a34f", + "0x6d756473746f726500000000000000005461626c657300000000000000000000" ], - "data": "0x6d756473746f726500000000000000005461626c657300000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000004e616d6573706163654f776e6572000000000000000000000000000000000000000000000000000000000000000001c00014010014000000000000000000000000000000000000000000000000000000001001004f000000000000000000000000000000000000000000000000000000001401006100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000a00000000000014000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000096e616d657370616365000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000056f776e6572000000000000000000000000000000000000000000000000000000", - "blockHash": "0xfe43f23153c69149ebe20b5941cccf8c55f4bf3acd82b33445ace71a2c5f8a9c", - "blockNumber": "0x2", - "transactionHash": "0xae82b15a8c62db1e23d9c523e730d9b861eed20529b8cff4d3452f86ae3149df", - "transactionIndex": "0x7", - "logIndex": "0x3", + "data": "0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000a000000000a00000000000014000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000004e616d6573706163654f776e6572000000000000000000000000000000000000000000000000000000000000000000600014010014000000000000000000000000000000000000000000000000000000001001004f0000000000000000000000000000000000000000000000000000000014010061000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000096e616d657370616365000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000056f776e6572000000000000000000000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0xa4f8502ae902c2b982a75bee37ad2c52b7456d38cb4fdd2288dbda0d53b5db23", + "transactionIndex": "0x0", + "logIndex": "0x2", "transactionLogIndex": "0x2", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0x912af873e852235aae78a1d25ae9bb28b616a67c36898c53a14fd8184504ee32" + "0xc851f86da4b2f7d69c86fc8dc136b05aa9805e49c225918a8856af392597a34f", + "0x6d756473746f726500000000000000005461626c657300000000000000000000" ], - "data": "0x6d756473746f726500000000000000005461626c657300000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000042616c616e636573000000000000000000000000000000000000000000000000000000000000000000000000000001c00020010020000000000000000000000000000000000000000000000000000000001001004f000000000000000000000000000000000000000000000000000000002001001f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000a00000000000014000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000096e616d6573706163650000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000762616c616e636500000000000000000000000000000000000000000000000000", - "blockHash": "0xfe43f23153c69149ebe20b5941cccf8c55f4bf3acd82b33445ace71a2c5f8a9c", - "blockNumber": "0x2", - "transactionHash": "0xae82b15a8c62db1e23d9c523e730d9b861eed20529b8cff4d3452f86ae3149df", - "transactionIndex": "0x7", - "logIndex": "0x4", + "data": "0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000a000000000a000000000000140000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000042616c616e636573000000000000000000000000000000000000000000000000000000000000000000000000000000600020010020000000000000000000000000000000000000000000000000000000001001004f000000000000000000000000000000000000000000000000000000002001001f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000096e616d6573706163650000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000762616c616e636500000000000000000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0xa4f8502ae902c2b982a75bee37ad2c52b7456d38cb4fdd2288dbda0d53b5db23", + "transactionIndex": "0x0", + "logIndex": "0x3", "transactionLogIndex": "0x3", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0x912af873e852235aae78a1d25ae9bb28b616a67c36898c53a14fd8184504ee32" + "0xc851f86da4b2f7d69c86fc8dc136b05aa9805e49c225918a8856af392597a34f", + "0x6d756473746f726500000000000000005461626c657300000000000000000000" ], - "data": "0x6d756473746f726500000000000000005461626c657300000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000496e7374616c6c65644d6f64756c657300000000000000000000000000000000000000000000000000000000000002200014010014000000000000000000000000000000000000000000000000000000003002004f5f0000000000000000000000000000000000000000000000000000001401006100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000100000000000001a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000a6d6f64756c654e616d6500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d617267756d656e74734861736800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d6d6f64756c654164647265737300000000000000000000000000000000000000", - "blockHash": "0xfe43f23153c69149ebe20b5941cccf8c55f4bf3acd82b33445ace71a2c5f8a9c", - "blockNumber": "0x2", - "transactionHash": "0xae82b15a8c62db1e23d9c523e730d9b861eed20529b8cff4d3452f86ae3149df", - "transactionIndex": "0x7", - "logIndex": "0x5", + "data": "0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000a00000000100000000000001a00000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000496e7374616c6c65644d6f64756c657300000000000000000000000000000000000000000000000000000000000000600014010014000000000000000000000000000000000000000000000000000000003002004f5f0000000000000000000000000000000000000000000000000000001401006100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000a6d6f64756c654e616d6500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d617267756d656e74734861736800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d6d6f64756c654164647265737300000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0xa4f8502ae902c2b982a75bee37ad2c52b7456d38cb4fdd2288dbda0d53b5db23", + "transactionIndex": "0x0", + "logIndex": "0x4", "transactionLogIndex": "0x4", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0x912af873e852235aae78a1d25ae9bb28b616a67c36898c53a14fd8184504ee32" + "0xc851f86da4b2f7d69c86fc8dc136b05aa9805e49c225918a8856af392597a34f", + "0x6d756473746f726500000000000000005461626c657300000000000000000000" ], - "data": "0x6d756473746f726500000000000000005461626c657300000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000044656c65676174696f6e730000000000000000000000000000000000000000000000000000000000000000000000022000200100200000000000000000000000000000000000000000000000000000000028020061610000000000000000000000000000000000000000000000000000002001005f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000100000000000001a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000964656c656761746f720000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000964656c6567617465650000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001364656c65676174696f6e436f6e74726f6c496400000000000000000000000000", - "blockHash": "0xfe43f23153c69149ebe20b5941cccf8c55f4bf3acd82b33445ace71a2c5f8a9c", - "blockNumber": "0x2", - "transactionHash": "0xae82b15a8c62db1e23d9c523e730d9b861eed20529b8cff4d3452f86ae3149df", - "transactionIndex": "0x7", - "logIndex": "0x6", + "data": "0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000a00000000100000000000001a0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000044656c65676174696f6e730000000000000000000000000000000000000000000000000000000000000000000000006000200100200000000000000000000000000000000000000000000000000000000028020061610000000000000000000000000000000000000000000000000000002001005f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000964656c656761746f720000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000964656c6567617465650000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001364656c65676174696f6e436f6e74726f6c496400000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0xa4f8502ae902c2b982a75bee37ad2c52b7456d38cb4fdd2288dbda0d53b5db23", + "transactionIndex": "0x0", + "logIndex": "0x5", "transactionLogIndex": "0x5", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0x912af873e852235aae78a1d25ae9bb28b616a67c36898c53a14fd8184504ee32" + "0xc851f86da4b2f7d69c86fc8dc136b05aa9805e49c225918a8856af392597a34f", + "0x6d756473746f726500000000000000005461626c657300000000000000000000" ], - "data": "0x6d756473746f726500000000000000005461626c657300000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000005265736f75726365416363657373000000000000000000000000000000000000000000000000000000000000000002200001010001000000000000000000000000000000000000000000000000000000003402005f610000000000000000000000000000000000000000000000000000000101006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000100000000000001a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000107265736f7572636553656c6563746f7200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000663616c6c6572000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000066163636573730000000000000000000000000000000000000000000000000000", - "blockHash": "0xfe43f23153c69149ebe20b5941cccf8c55f4bf3acd82b33445ace71a2c5f8a9c", - "blockNumber": "0x2", - "transactionHash": "0xae82b15a8c62db1e23d9c523e730d9b861eed20529b8cff4d3452f86ae3149df", - "transactionIndex": "0x7", - "logIndex": "0x7", + "data": "0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000a00000000100000000000001a000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000005265736f75726365416363657373000000000000000000000000000000000000000000000000000000000000000000600001010001000000000000000000000000000000000000000000000000000000003402005f610000000000000000000000000000000000000000000000000000000101006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000107265736f7572636553656c6563746f7200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000663616c6c6572000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000066163636573730000000000000000000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0xa4f8502ae902c2b982a75bee37ad2c52b7456d38cb4fdd2288dbda0d53b5db23", + "transactionIndex": "0x0", + "logIndex": "0x6", "transactionLogIndex": "0x6", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0x912af873e852235aae78a1d25ae9bb28b616a67c36898c53a14fd8184504ee32" + "0xc851f86da4b2f7d69c86fc8dc136b05aa9805e49c225918a8856af392597a34f", + "0x6d756473746f726500000000000000005461626c657300000000000000000000" ], - "data": "0x6d756473746f726500000000000000005461626c657300000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000053797374656d7300000000000000000000000000000000000000000000000000000000000000000000000000000002200015020014010000000000000000000000000000000000000000000000000000002001005f0000000000000000000000000000000000000000000000000000000015020061600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000a0000000000001a000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000107265736f7572636553656c6563746f72000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000673797374656d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c7075626c69634163636573730000000000000000000000000000000000000000", - "blockHash": "0xfe43f23153c69149ebe20b5941cccf8c55f4bf3acd82b33445ace71a2c5f8a9c", - "blockNumber": "0x2", - "transactionHash": "0xae82b15a8c62db1e23d9c523e730d9b861eed20529b8cff4d3452f86ae3149df", - "transactionIndex": "0x7", - "logIndex": "0x8", + "data": "0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000010000000000a0000000000001a0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000053797374656d7300000000000000000000000000000000000000000000000000000000000000000000000000000000600015020014010000000000000000000000000000000000000000000000000000002001005f000000000000000000000000000000000000000000000000000000001502006160000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000107265736f7572636553656c6563746f72000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000673797374656d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c7075626c69634163636573730000000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0xa4f8502ae902c2b982a75bee37ad2c52b7456d38cb4fdd2288dbda0d53b5db23", + "transactionIndex": "0x0", + "logIndex": "0x7", "transactionLogIndex": "0x7", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0x912af873e852235aae78a1d25ae9bb28b616a67c36898c53a14fd8184504ee32" + "0xc851f86da4b2f7d69c86fc8dc136b05aa9805e49c225918a8856af392597a34f", + "0x6d756473746f726500000000000000005461626c657300000000000000000000" ], - "data": "0x6d756473746f726500000000000000005461626c657300000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000046756e6374696f6e53656c6563746f72000000000000000000000000000000000000000000000000000000000000022000240200200400000000000000000000000000000000000000000000000000000004010043000000000000000000000000000000000000000000000000000000002402005f430000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000a0000000000001a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001066756e6374696f6e53656c6563746f7200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000107265736f7572636553656c6563746f7200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001673797374656d46756e6374696f6e53656c6563746f7200000000000000000000", - "blockHash": "0xfe43f23153c69149ebe20b5941cccf8c55f4bf3acd82b33445ace71a2c5f8a9c", - "blockNumber": "0x2", - "transactionHash": "0xae82b15a8c62db1e23d9c523e730d9b861eed20529b8cff4d3452f86ae3149df", - "transactionIndex": "0x7", - "logIndex": "0x9", + "data": "0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000010000000000a0000000000001a0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000046756e6374696f6e53656c6563746f72000000000000000000000000000000000000000000000000000000000000006000240200200400000000000000000000000000000000000000000000000000000004010043000000000000000000000000000000000000000000000000000000002402005f43000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001066756e6374696f6e53656c6563746f7200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000107265736f7572636553656c6563746f7200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001673797374656d46756e6374696f6e53656c6563746f7200000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0xa4f8502ae902c2b982a75bee37ad2c52b7456d38cb4fdd2288dbda0d53b5db23", + "transactionIndex": "0x0", + "logIndex": "0x8", "transactionLogIndex": "0x8", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0x912af873e852235aae78a1d25ae9bb28b616a67c36898c53a14fd8184504ee32" + "0xc851f86da4b2f7d69c86fc8dc136b05aa9805e49c225918a8856af392597a34f", + "0x6d756473746f726500000000000000005461626c657300000000000000000000" ], - "data": "0x6d756473746f726500000000000000005461626c657300000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000053797374656d486f6f6b73000000000000000000000000000000000000000000000000000000000000000000000001c00000000100000000000000000000000000000000000000000000000000000000002001005f00000000000000000000000000000000000000000000000000000000000001b600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000a00000000000014000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000107265736f7572636553656c6563746f7200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000576616c7565000000000000000000000000000000000000000000000000000000", - "blockHash": "0xfe43f23153c69149ebe20b5941cccf8c55f4bf3acd82b33445ace71a2c5f8a9c", - "blockNumber": "0x2", - "transactionHash": "0xae82b15a8c62db1e23d9c523e730d9b861eed20529b8cff4d3452f86ae3149df", - "transactionIndex": "0x7", - "logIndex": "0xa", + "data": "0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000a000000000a000000000000140000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000053797374656d486f6f6b73000000000000000000000000000000000000000000000000000000000000000000000000600000000100000000000000000000000000000000000000000000000000000000002001005f00000000000000000000000000000000000000000000000000000000000001b6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000107265736f7572636553656c6563746f7200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000576616c7565000000000000000000000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0xa4f8502ae902c2b982a75bee37ad2c52b7456d38cb4fdd2288dbda0d53b5db23", + "transactionIndex": "0x0", + "logIndex": "0x9", "transactionLogIndex": "0x9", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0x912af873e852235aae78a1d25ae9bb28b616a67c36898c53a14fd8184504ee32" + "0xc851f86da4b2f7d69c86fc8dc136b05aa9805e49c225918a8856af392597a34f", + "0x6d756473746f726500000000000000005461626c657300000000000000000000" ], - "data": "0x6d756473746f726500000000000000005461626c657300000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000053797374656d5265676973747279000000000000000000000000000000000000000000000000000000000000000001c000200100200000000000000000000000000000000000000000000000000000000014010061000000000000000000000000000000000000000000000000000000002001005f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000a000000000000140000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000673797374656d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000107265736f7572636553656c6563746f7200000000000000000000000000000000", - "blockHash": "0xfe43f23153c69149ebe20b5941cccf8c55f4bf3acd82b33445ace71a2c5f8a9c", - "blockNumber": "0x2", - "transactionHash": "0xae82b15a8c62db1e23d9c523e730d9b861eed20529b8cff4d3452f86ae3149df", - "transactionIndex": "0x7", - "logIndex": "0xb", + "data": "0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000a000000000a000000000000140000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000053797374656d52656769737472790000000000000000000000000000000000000000000000000000000000000000006000200100200000000000000000000000000000000000000000000000000000000014010061000000000000000000000000000000000000000000000000000000002001005f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000673797374656d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000107265736f7572636553656c6563746f7200000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0xa4f8502ae902c2b982a75bee37ad2c52b7456d38cb4fdd2288dbda0d53b5db23", + "transactionIndex": "0x0", + "logIndex": "0xa", "transactionLogIndex": "0xa", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0x912af873e852235aae78a1d25ae9bb28b616a67c36898c53a14fd8184504ee32" + "0xc851f86da4b2f7d69c86fc8dc136b05aa9805e49c225918a8856af392597a34f", + "0x6d756473746f726500000000000000005461626c657300000000000000000000" ], - "data": "0x6d756473746f726500000000000000005461626c657300000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000005265736f75726365547970650000000000000000000000000000000000000000000000000000000000000000000001c00001010001000000000000000000000000000000000000000000000000000000002001005f000000000000000000000000000000000000000000000000000000000101000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000a00000000000014000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000107265736f7572636553656c6563746f7200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c7265736f75726365547970650000000000000000000000000000000000000000", - "blockHash": "0xfe43f23153c69149ebe20b5941cccf8c55f4bf3acd82b33445ace71a2c5f8a9c", - "blockNumber": "0x2", - "transactionHash": "0xae82b15a8c62db1e23d9c523e730d9b861eed20529b8cff4d3452f86ae3149df", - "transactionIndex": "0x7", - "logIndex": "0xc", + "data": "0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000a000000000a00000000000014000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000005265736f75726365547970650000000000000000000000000000000000000000000000000000000000000000000000600001010001000000000000000000000000000000000000000000000000000000002001005f0000000000000000000000000000000000000000000000000000000001010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000107265736f7572636553656c6563746f7200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c7265736f75726365547970650000000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0xa4f8502ae902c2b982a75bee37ad2c52b7456d38cb4fdd2288dbda0d53b5db23", + "transactionIndex": "0x0", + "logIndex": "0xb", "transactionLogIndex": "0xb", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0xd01f9f1368f831528fc9fe6442366b2b7d957fbfff3bcf7c24d9ab5fe51f8c46" + "0xe7b614a59a30dff5ea0536e50c4019312e856b636126e1b2d19a7c2872798735", + "0x000000000000000000000000000000004e616d6573706163654f776e65720000" ], - "data": "0x000000000000000000000000000000004e616d6573706163654f776e657200000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014f39fd6e51aad88f6f4ce6ab8827279cfffb92266000000000000000000000000", - "blockHash": "0xfe43f23153c69149ebe20b5941cccf8c55f4bf3acd82b33445ace71a2c5f8a9c", - "blockNumber": "0x2", - "transactionHash": "0xae82b15a8c62db1e23d9c523e730d9b861eed20529b8cff4d3452f86ae3149df", - "transactionIndex": "0x7", - "logIndex": "0xd", + "data": "0x00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014f39fd6e51aad88f6f4ce6ab8827279cfffb92266000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0xa4f8502ae902c2b982a75bee37ad2c52b7456d38cb4fdd2288dbda0d53b5db23", + "transactionIndex": "0x0", + "logIndex": "0xc", "transactionLogIndex": "0xc", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0xd01f9f1368f831528fc9fe6442366b2b7d957fbfff3bcf7c24d9ab5fe51f8c46" + "0xe7b614a59a30dff5ea0536e50c4019312e856b636126e1b2d19a7c2872798735", + "0x000000000000000000000000000000005265736f757263654163636573730000" ], - "data": "0x000000000000000000000000000000005265736f7572636541636365737300000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb9226600000000000000000000000000000000000000000000000000000000000000010100000000000000000000000000000000000000000000000000000000000000", - "blockHash": "0xfe43f23153c69149ebe20b5941cccf8c55f4bf3acd82b33445ace71a2c5f8a9c", - "blockNumber": "0x2", - "transactionHash": "0xae82b15a8c62db1e23d9c523e730d9b861eed20529b8cff4d3452f86ae3149df", - "transactionIndex": "0x7", - "logIndex": "0xe", + "data": "0x00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb9226600000000000000000000000000000000000000000000000000000000000000010100000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0xa4f8502ae902c2b982a75bee37ad2c52b7456d38cb4fdd2288dbda0d53b5db23", + "transactionIndex": "0x0", + "logIndex": "0xd", "transactionLogIndex": "0xd", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0xd01f9f1368f831528fc9fe6442366b2b7d957fbfff3bcf7c24d9ab5fe51f8c46" + "0xe7b614a59a30dff5ea0536e50c4019312e856b636126e1b2d19a7c2872798735", + "0x000000000000000000000000000000005265736f757263655479706500000000" ], - "data": "0x000000000000000000000000000000005265736f7572636554797065000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010100000000000000000000000000000000000000000000000000000000000000", - "blockHash": "0xfe43f23153c69149ebe20b5941cccf8c55f4bf3acd82b33445ace71a2c5f8a9c", - "blockNumber": "0x2", - "transactionHash": "0xae82b15a8c62db1e23d9c523e730d9b861eed20529b8cff4d3452f86ae3149df", - "transactionIndex": "0x7", - "logIndex": "0xf", + "data": "0x00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010100000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0xa4f8502ae902c2b982a75bee37ad2c52b7456d38cb4fdd2288dbda0d53b5db23", + "transactionIndex": "0x0", + "logIndex": "0xe", "transactionLogIndex": "0xe", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0xd01f9f1368f831528fc9fe6442366b2b7d957fbfff3bcf7c24d9ab5fe51f8c46" + "0xe7b614a59a30dff5ea0536e50c4019312e856b636126e1b2d19a7c2872798735", + "0x000000000000000000000000000000005265736f757263655479706500000000" ], - "data": "0x000000000000000000000000000000005265736f7572636554797065000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000636f72652e730000000000000000000000000000000000000000000000000000000000000000000000000000000000010300000000000000000000000000000000000000000000000000000000000000", - "blockHash": "0xfe43f23153c69149ebe20b5941cccf8c55f4bf3acd82b33445ace71a2c5f8a9c", - "blockNumber": "0x2", - "transactionHash": "0xae82b15a8c62db1e23d9c523e730d9b861eed20529b8cff4d3452f86ae3149df", - "transactionIndex": "0x7", - "logIndex": "0x10", + "data": "0x00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000636f72652e730000000000000000000000000000000000000000000000000000000000000000000000000000000000010300000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0xa4f8502ae902c2b982a75bee37ad2c52b7456d38cb4fdd2288dbda0d53b5db23", + "transactionIndex": "0x0", + "logIndex": "0xf", "transactionLogIndex": "0xf", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0x912af873e852235aae78a1d25ae9bb28b616a67c36898c53a14fd8184504ee32" + "0xc851f86da4b2f7d69c86fc8dc136b05aa9805e49c225918a8856af392597a34f", + "0x0000000000000000000000000000000053797374656d73000000000000000000" ], - "data": "0x0000000000000000000000000000000053797374656d73000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000636f72652e73000000000000000000000000000000000000000000000000000000000000000000000000000000000015cafac3dd18ac6c6e92c921884f9e4176737c052c010000000000000000000000", - "blockHash": "0xfe43f23153c69149ebe20b5941cccf8c55f4bf3acd82b33445ace71a2c5f8a9c", - "blockNumber": "0x2", - "transactionHash": "0xae82b15a8c62db1e23d9c523e730d9b861eed20529b8cff4d3452f86ae3149df", - "transactionIndex": "0x7", - "logIndex": "0x11", + "data": "0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000636f72652e73000000000000000000000000000000000000000000000000000000000000000000000000000000000015cafac3dd18ac6c6e92c921884f9e4176737c052c0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0xa4f8502ae902c2b982a75bee37ad2c52b7456d38cb4fdd2288dbda0d53b5db23", + "transactionIndex": "0x0", + "logIndex": "0x10", "transactionLogIndex": "0x10", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0xd01f9f1368f831528fc9fe6442366b2b7d957fbfff3bcf7c24d9ab5fe51f8c46" + "0xe7b614a59a30dff5ea0536e50c4019312e856b636126e1b2d19a7c2872798735", + "0x0000000000000000000000000000000053797374656d52656769737472790000" ], - "data": "0x0000000000000000000000000000000053797374656d526567697374727900000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000cafac3dd18ac6c6e92c921884f9e4176737c052c000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000636f72652e7300000000000000000000", - "blockHash": "0xfe43f23153c69149ebe20b5941cccf8c55f4bf3acd82b33445ace71a2c5f8a9c", - "blockNumber": "0x2", - "transactionHash": "0xae82b15a8c62db1e23d9c523e730d9b861eed20529b8cff4d3452f86ae3149df", - "transactionIndex": "0x7", - "logIndex": "0x12", + "data": "0x00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000cafac3dd18ac6c6e92c921884f9e4176737c052c000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000636f72652e7300000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0xa4f8502ae902c2b982a75bee37ad2c52b7456d38cb4fdd2288dbda0d53b5db23", + "transactionIndex": "0x0", + "logIndex": "0x11", "transactionLogIndex": "0x11", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0xd01f9f1368f831528fc9fe6442366b2b7d957fbfff3bcf7c24d9ab5fe51f8c46" + "0xe7b614a59a30dff5ea0536e50c4019312e856b636126e1b2d19a7c2872798735", + "0x000000000000000000000000000000005265736f757263654163636573730000" ], - "data": "0x000000000000000000000000000000005265736f7572636541636365737300000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cafac3dd18ac6c6e92c921884f9e4176737c052c00000000000000000000000000000000000000000000000000000000000000010100000000000000000000000000000000000000000000000000000000000000", - "blockHash": "0xfe43f23153c69149ebe20b5941cccf8c55f4bf3acd82b33445ace71a2c5f8a9c", - "blockNumber": "0x2", - "transactionHash": "0xae82b15a8c62db1e23d9c523e730d9b861eed20529b8cff4d3452f86ae3149df", - "transactionIndex": "0x7", - "logIndex": "0x13", + "data": "0x00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cafac3dd18ac6c6e92c921884f9e4176737c052c00000000000000000000000000000000000000000000000000000000000000010100000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0xa4f8502ae902c2b982a75bee37ad2c52b7456d38cb4fdd2288dbda0d53b5db23", + "transactionIndex": "0x0", + "logIndex": "0x12", "transactionLogIndex": "0x12", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0x912af873e852235aae78a1d25ae9bb28b616a67c36898c53a14fd8184504ee32" + "0xc851f86da4b2f7d69c86fc8dc136b05aa9805e49c225918a8856af392597a34f", + "0x0000000000000000000000000000000046756e6374696f6e53656c6563746f72" ], - "data": "0x0000000000000000000000000000000046756e6374696f6e53656c6563746f72000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000140554c3a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000636f72652e730000000000000000000040554c3a00000000000000000000000000000000000000000000000000000000", - "blockHash": "0xfe43f23153c69149ebe20b5941cccf8c55f4bf3acd82b33445ace71a2c5f8a9c", - "blockNumber": "0x2", - "transactionHash": "0xae82b15a8c62db1e23d9c523e730d9b861eed20529b8cff4d3452f86ae3149df", - "transactionIndex": "0x7", - "logIndex": "0x14", + "data": "0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000140554c3a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000636f72652e730000000000000000000040554c3a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0xa4f8502ae902c2b982a75bee37ad2c52b7456d38cb4fdd2288dbda0d53b5db23", + "transactionIndex": "0x0", + "logIndex": "0x13", "transactionLogIndex": "0x13", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0x912af873e852235aae78a1d25ae9bb28b616a67c36898c53a14fd8184504ee32" + "0xc851f86da4b2f7d69c86fc8dc136b05aa9805e49c225918a8856af392597a34f", + "0x0000000000000000000000000000000046756e6374696f6e53656c6563746f72" ], - "data": "0x0000000000000000000000000000000046756e6374696f6e53656c6563746f72000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000018d53b20800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000636f72652e73000000000000000000008d53b20800000000000000000000000000000000000000000000000000000000", - "blockHash": "0xfe43f23153c69149ebe20b5941cccf8c55f4bf3acd82b33445ace71a2c5f8a9c", - "blockNumber": "0x2", - "transactionHash": "0xae82b15a8c62db1e23d9c523e730d9b861eed20529b8cff4d3452f86ae3149df", - "transactionIndex": "0x7", - "logIndex": "0x15", + "data": "0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000018d53b20800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000636f72652e73000000000000000000008d53b208000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0xa4f8502ae902c2b982a75bee37ad2c52b7456d38cb4fdd2288dbda0d53b5db23", + "transactionIndex": "0x0", + "logIndex": "0x14", "transactionLogIndex": "0x14", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0x912af873e852235aae78a1d25ae9bb28b616a67c36898c53a14fd8184504ee32" + "0xc851f86da4b2f7d69c86fc8dc136b05aa9805e49c225918a8856af392597a34f", + "0x0000000000000000000000000000000046756e6374696f6e53656c6563746f72" ], - "data": "0x0000000000000000000000000000000046756e6374696f6e53656c6563746f72000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000125f6221000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000636f72652e730000000000000000000025f6221000000000000000000000000000000000000000000000000000000000", - "blockHash": "0xfe43f23153c69149ebe20b5941cccf8c55f4bf3acd82b33445ace71a2c5f8a9c", - "blockNumber": "0x2", - "transactionHash": "0xae82b15a8c62db1e23d9c523e730d9b861eed20529b8cff4d3452f86ae3149df", - "transactionIndex": "0x7", - "logIndex": "0x16", + "data": "0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000125f6221000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000636f72652e730000000000000000000025f62210000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0xa4f8502ae902c2b982a75bee37ad2c52b7456d38cb4fdd2288dbda0d53b5db23", + "transactionIndex": "0x0", + "logIndex": "0x15", "transactionLogIndex": "0x15", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0x912af873e852235aae78a1d25ae9bb28b616a67c36898c53a14fd8184504ee32" + "0xc851f86da4b2f7d69c86fc8dc136b05aa9805e49c225918a8856af392597a34f", + "0x0000000000000000000000000000000046756e6374696f6e53656c6563746f72" ], - "data": "0x0000000000000000000000000000000046756e6374696f6e53656c6563746f72000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000012bfaa27400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000636f72652e73000000000000000000002bfaa27400000000000000000000000000000000000000000000000000000000", - "blockHash": "0xfe43f23153c69149ebe20b5941cccf8c55f4bf3acd82b33445ace71a2c5f8a9c", - "blockNumber": "0x2", - "transactionHash": "0xae82b15a8c62db1e23d9c523e730d9b861eed20529b8cff4d3452f86ae3149df", - "transactionIndex": "0x7", - "logIndex": "0x17", + "data": "0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000012bfaa27400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000636f72652e73000000000000000000002bfaa274000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0xa4f8502ae902c2b982a75bee37ad2c52b7456d38cb4fdd2288dbda0d53b5db23", + "transactionIndex": "0x0", + "logIndex": "0x16", "transactionLogIndex": "0x16", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0x912af873e852235aae78a1d25ae9bb28b616a67c36898c53a14fd8184504ee32" + "0xc851f86da4b2f7d69c86fc8dc136b05aa9805e49c225918a8856af392597a34f", + "0x0000000000000000000000000000000046756e6374696f6e53656c6563746f72" ], - "data": "0x0000000000000000000000000000000046756e6374696f6e53656c6563746f72000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000121293ca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000636f72652e730000000000000000000021293ca000000000000000000000000000000000000000000000000000000000", - "blockHash": "0xfe43f23153c69149ebe20b5941cccf8c55f4bf3acd82b33445ace71a2c5f8a9c", - "blockNumber": "0x2", - "transactionHash": "0xae82b15a8c62db1e23d9c523e730d9b861eed20529b8cff4d3452f86ae3149df", - "transactionIndex": "0x7", - "logIndex": "0x18", + "data": "0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000121293ca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000636f72652e730000000000000000000021293ca0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0xa4f8502ae902c2b982a75bee37ad2c52b7456d38cb4fdd2288dbda0d53b5db23", + "transactionIndex": "0x0", + "logIndex": "0x17", "transactionLogIndex": "0x17", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0x912af873e852235aae78a1d25ae9bb28b616a67c36898c53a14fd8184504ee32" + "0xc851f86da4b2f7d69c86fc8dc136b05aa9805e49c225918a8856af392597a34f", + "0x0000000000000000000000000000000046756e6374696f6e53656c6563746f72" ], - "data": "0x0000000000000000000000000000000046756e6374696f6e53656c6563746f72000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000018818929400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000636f72652e73000000000000000000008818929400000000000000000000000000000000000000000000000000000000", - "blockHash": "0xfe43f23153c69149ebe20b5941cccf8c55f4bf3acd82b33445ace71a2c5f8a9c", - "blockNumber": "0x2", - "transactionHash": "0xae82b15a8c62db1e23d9c523e730d9b861eed20529b8cff4d3452f86ae3149df", - "transactionIndex": "0x7", - "logIndex": "0x19", + "data": "0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000013ea8492200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000636f72652e73000000000000000000003ea84922000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0xa4f8502ae902c2b982a75bee37ad2c52b7456d38cb4fdd2288dbda0d53b5db23", + "transactionIndex": "0x0", + "logIndex": "0x18", "transactionLogIndex": "0x18", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0x912af873e852235aae78a1d25ae9bb28b616a67c36898c53a14fd8184504ee32" + "0xc851f86da4b2f7d69c86fc8dc136b05aa9805e49c225918a8856af392597a34f", + "0x0000000000000000000000000000000046756e6374696f6e53656c6563746f72" ], - "data": "0x0000000000000000000000000000000046756e6374696f6e53656c6563746f72000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000018da798da00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000636f72652e73000000000000000000008da798da00000000000000000000000000000000000000000000000000000000", - "blockHash": "0xfe43f23153c69149ebe20b5941cccf8c55f4bf3acd82b33445ace71a2c5f8a9c", - "blockNumber": "0x2", - "transactionHash": "0xae82b15a8c62db1e23d9c523e730d9b861eed20529b8cff4d3452f86ae3149df", - "transactionIndex": "0x7", - "logIndex": "0x1a", + "data": "0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000018da798da00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000636f72652e73000000000000000000008da798da000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0xa4f8502ae902c2b982a75bee37ad2c52b7456d38cb4fdd2288dbda0d53b5db23", + "transactionIndex": "0x0", + "logIndex": "0x19", "transactionLogIndex": "0x19", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0x912af873e852235aae78a1d25ae9bb28b616a67c36898c53a14fd8184504ee32" + "0xc851f86da4b2f7d69c86fc8dc136b05aa9805e49c225918a8856af392597a34f", + "0x0000000000000000000000000000000046756e6374696f6e53656c6563746f72" ], - "data": "0x0000000000000000000000000000000046756e6374696f6e53656c6563746f72000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010ba51f4900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000636f72652e73000000000000000000000ba51f4900000000000000000000000000000000000000000000000000000000", - "blockHash": "0xfe43f23153c69149ebe20b5941cccf8c55f4bf3acd82b33445ace71a2c5f8a9c", - "blockNumber": "0x2", - "transactionHash": "0xae82b15a8c62db1e23d9c523e730d9b861eed20529b8cff4d3452f86ae3149df", - "transactionIndex": "0x7", - "logIndex": "0x1b", + "data": "0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000010ba51f4900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000636f72652e73000000000000000000000ba51f49000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0xa4f8502ae902c2b982a75bee37ad2c52b7456d38cb4fdd2288dbda0d53b5db23", + "transactionIndex": "0x0", + "logIndex": "0x1a", "transactionLogIndex": "0x1a", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0x912af873e852235aae78a1d25ae9bb28b616a67c36898c53a14fd8184504ee32" + "0xc851f86da4b2f7d69c86fc8dc136b05aa9805e49c225918a8856af392597a34f", + "0x0000000000000000000000000000000046756e6374696f6e53656c6563746f72" ], - "data": "0x0000000000000000000000000000000046756e6374696f6e53656c6563746f72000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001530f4b6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000636f72652e7300000000000000000000530f4b6000000000000000000000000000000000000000000000000000000000", - "blockHash": "0xfe43f23153c69149ebe20b5941cccf8c55f4bf3acd82b33445ace71a2c5f8a9c", - "blockNumber": "0x2", - "transactionHash": "0xae82b15a8c62db1e23d9c523e730d9b861eed20529b8cff4d3452f86ae3149df", - "transactionIndex": "0x7", - "logIndex": "0x1c", + "data": "0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000001530f4b6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000636f72652e7300000000000000000000530f4b60000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0xa4f8502ae902c2b982a75bee37ad2c52b7456d38cb4fdd2288dbda0d53b5db23", + "transactionIndex": "0x0", + "logIndex": "0x1b", "transactionLogIndex": "0x1b", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0x912af873e852235aae78a1d25ae9bb28b616a67c36898c53a14fd8184504ee32" + "0xc851f86da4b2f7d69c86fc8dc136b05aa9805e49c225918a8856af392597a34f", + "0x0000000000000000000000000000000046756e6374696f6e53656c6563746f72" ], - "data": "0x0000000000000000000000000000000046756e6374696f6e53656c6563746f72000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010560912900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000636f72652e73000000000000000000000560912900000000000000000000000000000000000000000000000000000000", - "blockHash": "0xfe43f23153c69149ebe20b5941cccf8c55f4bf3acd82b33445ace71a2c5f8a9c", - "blockNumber": "0x2", - "transactionHash": "0xae82b15a8c62db1e23d9c523e730d9b861eed20529b8cff4d3452f86ae3149df", - "transactionIndex": "0x7", - "logIndex": "0x1d", + "data": "0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000010560912900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000636f72652e730000000000000000000005609129000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0xa4f8502ae902c2b982a75bee37ad2c52b7456d38cb4fdd2288dbda0d53b5db23", + "transactionIndex": "0x0", + "logIndex": "0x1c", "transactionLogIndex": "0x1c", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0x912af873e852235aae78a1d25ae9bb28b616a67c36898c53a14fd8184504ee32" + "0xc851f86da4b2f7d69c86fc8dc136b05aa9805e49c225918a8856af392597a34f", + "0x0000000000000000000000000000000046756e6374696f6e53656c6563746f72" ], - "data": "0x0000000000000000000000000000000046756e6374696f6e53656c6563746f72000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a886545e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000636f72652e7300000000000000000000a886545e00000000000000000000000000000000000000000000000000000000", - "blockHash": "0xfe43f23153c69149ebe20b5941cccf8c55f4bf3acd82b33445ace71a2c5f8a9c", - "blockNumber": "0x2", - "transactionHash": "0xae82b15a8c62db1e23d9c523e730d9b861eed20529b8cff4d3452f86ae3149df", - "transactionIndex": "0x7", - "logIndex": "0x1e", + "data": "0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000001a886545e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000636f72652e7300000000000000000000a886545e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0xa4f8502ae902c2b982a75bee37ad2c52b7456d38cb4fdd2288dbda0d53b5db23", + "transactionIndex": "0x0", + "logIndex": "0x1d", "transactionLogIndex": "0x1d", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0x912af873e852235aae78a1d25ae9bb28b616a67c36898c53a14fd8184504ee32" + "0xc851f86da4b2f7d69c86fc8dc136b05aa9805e49c225918a8856af392597a34f", + "0x0000000000000000000000000000000046756e6374696f6e53656c6563746f72" ], - "data": "0x0000000000000000000000000000000046756e6374696f6e53656c6563746f72000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001d5f8337f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000636f72652e7300000000000000000000d5f8337f00000000000000000000000000000000000000000000000000000000", - "blockHash": "0xfe43f23153c69149ebe20b5941cccf8c55f4bf3acd82b33445ace71a2c5f8a9c", - "blockNumber": "0x2", - "transactionHash": "0xae82b15a8c62db1e23d9c523e730d9b861eed20529b8cff4d3452f86ae3149df", - "transactionIndex": "0x7", - "logIndex": "0x1f", + "data": "0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000001d5f8337f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000636f72652e7300000000000000000000d5f8337f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0xa4f8502ae902c2b982a75bee37ad2c52b7456d38cb4fdd2288dbda0d53b5db23", + "transactionIndex": "0x0", + "logIndex": "0x1e", "transactionLogIndex": "0x1e", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0x912af873e852235aae78a1d25ae9bb28b616a67c36898c53a14fd8184504ee32" + "0xc851f86da4b2f7d69c86fc8dc136b05aa9805e49c225918a8856af392597a34f", + "0x0000000000000000000000000000000046756e6374696f6e53656c6563746f72" ], - "data": "0x0000000000000000000000000000000046756e6374696f6e53656c6563746f72000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a92813ad00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000636f72652e7300000000000000000000a92813ad00000000000000000000000000000000000000000000000000000000", - "blockHash": "0xfe43f23153c69149ebe20b5941cccf8c55f4bf3acd82b33445ace71a2c5f8a9c", - "blockNumber": "0x2", - "transactionHash": "0xae82b15a8c62db1e23d9c523e730d9b861eed20529b8cff4d3452f86ae3149df", - "transactionIndex": "0x7", - "logIndex": "0x20", + "data": "0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000001a92813ad00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000636f72652e7300000000000000000000a92813ad000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0xa4f8502ae902c2b982a75bee37ad2c52b7456d38cb4fdd2288dbda0d53b5db23", + "transactionIndex": "0x0", + "logIndex": "0x1f", "transactionLogIndex": "0x1f", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0x912af873e852235aae78a1d25ae9bb28b616a67c36898c53a14fd8184504ee32" + "0xc851f86da4b2f7d69c86fc8dc136b05aa9805e49c225918a8856af392597a34f", + "0x0000000000000000000000000000000046756e6374696f6e53656c6563746f72" ], - "data": "0x0000000000000000000000000000000046756e6374696f6e53656c6563746f72000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000013350b6a900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000636f72652e73000000000000000000003350b6a900000000000000000000000000000000000000000000000000000000", - "blockHash": "0xfe43f23153c69149ebe20b5941cccf8c55f4bf3acd82b33445ace71a2c5f8a9c", - "blockNumber": "0x2", - "transactionHash": "0xae82b15a8c62db1e23d9c523e730d9b861eed20529b8cff4d3452f86ae3149df", - "transactionIndex": "0x7", - "logIndex": "0x21", + "data": "0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000013350b6a900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000636f72652e73000000000000000000003350b6a9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0xa4f8502ae902c2b982a75bee37ad2c52b7456d38cb4fdd2288dbda0d53b5db23", + "transactionIndex": "0x0", + "logIndex": "0x20", "transactionLogIndex": "0x20", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0x912af873e852235aae78a1d25ae9bb28b616a67c36898c53a14fd8184504ee32" + "0xc851f86da4b2f7d69c86fc8dc136b05aa9805e49c225918a8856af392597a34f", + "0x0000000000000000000000000000000046756e6374696f6e53656c6563746f72" ], - "data": "0x0000000000000000000000000000000046756e6374696f6e53656c6563746f72000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000013c03a51c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000636f72652e73000000000000000000003c03a51c00000000000000000000000000000000000000000000000000000000", - "blockHash": "0xfe43f23153c69149ebe20b5941cccf8c55f4bf3acd82b33445ace71a2c5f8a9c", - "blockNumber": "0x2", - "transactionHash": "0xae82b15a8c62db1e23d9c523e730d9b861eed20529b8cff4d3452f86ae3149df", - "transactionIndex": "0x7", - "logIndex": "0x22", + "data": "0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000013c03a51c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000636f72652e73000000000000000000003c03a51c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0xa4f8502ae902c2b982a75bee37ad2c52b7456d38cb4fdd2288dbda0d53b5db23", + "transactionIndex": "0x0", + "logIndex": "0x21", "transactionLogIndex": "0x21", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0x912af873e852235aae78a1d25ae9bb28b616a67c36898c53a14fd8184504ee32" + "0xc851f86da4b2f7d69c86fc8dc136b05aa9805e49c225918a8856af392597a34f", + "0x0000000000000000000000000000000046756e6374696f6e53656c6563746f72" ], - "data": "0x0000000000000000000000000000000046756e6374696f6e53656c6563746f72000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001b7a3c75600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000636f72652e7300000000000000000000b7a3c75600000000000000000000000000000000000000000000000000000000", - "blockHash": "0xfe43f23153c69149ebe20b5941cccf8c55f4bf3acd82b33445ace71a2c5f8a9c", - "blockNumber": "0x2", - "transactionHash": "0xae82b15a8c62db1e23d9c523e730d9b861eed20529b8cff4d3452f86ae3149df", - "transactionIndex": "0x7", - "logIndex": "0x23", + "data": "0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000001b7a3c75600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000636f72652e7300000000000000000000b7a3c756000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0xa4f8502ae902c2b982a75bee37ad2c52b7456d38cb4fdd2288dbda0d53b5db23", + "transactionIndex": "0x0", + "logIndex": "0x22", "transactionLogIndex": "0x22", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0x912af873e852235aae78a1d25ae9bb28b616a67c36898c53a14fd8184504ee32" + "0xc851f86da4b2f7d69c86fc8dc136b05aa9805e49c225918a8856af392597a34f", + "0x0000000000000000000000000000000046756e6374696f6e53656c6563746f72" ], - "data": "0x0000000000000000000000000000000046756e6374696f6e53656c6563746f72000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000011d2257ba00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000636f72652e73000000000000000000001d2257ba00000000000000000000000000000000000000000000000000000000", - "blockHash": "0xfe43f23153c69149ebe20b5941cccf8c55f4bf3acd82b33445ace71a2c5f8a9c", - "blockNumber": "0x2", - "transactionHash": "0xae82b15a8c62db1e23d9c523e730d9b861eed20529b8cff4d3452f86ae3149df", - "transactionIndex": "0x7", - "logIndex": "0x24", + "data": "0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000011d2257ba00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000636f72652e73000000000000000000001d2257ba000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0xa4f8502ae902c2b982a75bee37ad2c52b7456d38cb4fdd2288dbda0d53b5db23", + "transactionIndex": "0x0", + "logIndex": "0x23", "transactionLogIndex": "0x23", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0x912af873e852235aae78a1d25ae9bb28b616a67c36898c53a14fd8184504ee32" + "0xe7b614a59a30dff5ea0536e50c4019312e856b636126e1b2d19a7c2872798735", + "0x00000000000000000000000000000000496e7374616c6c65644d6f64756c6573" ], - "data": "0x00000000000000000000000000000000496e7374616c6c65644d6f64756c6573000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000002636f72652e6d0000000000000000000000000000000000000000000000000000c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a4700000000000000000000000000000000000000000000000000000000000000014e7f1725e7734ce288f8367e1bb143e90bb3f0512000000000000000000000000", - "blockHash": "0xfe43f23153c69149ebe20b5941cccf8c55f4bf3acd82b33445ace71a2c5f8a9c", - "blockNumber": "0x2", - "transactionHash": "0xae82b15a8c62db1e23d9c523e730d9b861eed20529b8cff4d3452f86ae3149df", - "transactionIndex": "0x7", - "logIndex": "0x25", + "data": "0x00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000002636f72652e6d0000000000000000000000000000000000000000000000000000c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a4700000000000000000000000000000000000000000000000000000000000000014e7f1725e7734ce288f8367e1bb143e90bb3f0512000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0xa4f8502ae902c2b982a75bee37ad2c52b7456d38cb4fdd2288dbda0d53b5db23", + "transactionIndex": "0x0", + "logIndex": "0x24", "transactionLogIndex": "0x24", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0xd01f9f1368f831528fc9fe6442366b2b7d957fbfff3bcf7c24d9ab5fe51f8c46" + "0xe7b614a59a30dff5ea0536e50c4019312e856b636126e1b2d19a7c2872798735", + "0x000000000000000000000000000000005265736f757263655479706500000000" ], - "data": "0x000000000000000000000000000000005265736f7572636554797065000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000004e756d6265724c69737400000000000000000000000000000000000000000000000000000000000000000000000000010200000000000000000000000000000000000000000000000000000000000000", - "blockHash": "0x40b6d1b700f1f3dc080ebe2d54c3553e6419da61cfeb5a5e2e2272ebe9840b9a", - "blockNumber": "0x3", - "transactionHash": "0x7bca8d7dabf72bfa9ff127d2d2fac95a747b5cbd45e44d820789f14a6d340d2f", - "transactionIndex": "0x0", - "logIndex": "0x0", + "data": "0x00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000004e756d6265720000000000000000000000000000000000000000000000000000000000000000000000000000000000010200000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0x63c9c3f365b40de92c07c06e86f096433bfb2e968c3caec07df00fa2cd1a46d1", + "transactionIndex": "0x1", + "logIndex": "0x25", "transactionLogIndex": "0x0", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0x912af873e852235aae78a1d25ae9bb28b616a67c36898c53a14fd8184504ee32" + "0xc851f86da4b2f7d69c86fc8dc136b05aa9805e49c225918a8856af392597a34f", + "0x6d756473746f726500000000000000005461626c657300000000000000000000" ], - "data": "0x6d756473746f726500000000000000005461626c657300000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000004e756d6265724c697374000000000000000000000000000000000000000000000000000000000000000000000000016000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000040000000000000e000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000576616c7565000000000000000000000000000000000000000000000000000000", - "blockHash": "0x40b6d1b700f1f3dc080ebe2d54c3553e6419da61cfeb5a5e2e2272ebe9840b9a", - "blockNumber": "0x3", - "transactionHash": "0x7bca8d7dabf72bfa9ff127d2d2fac95a747b5cbd45e44d820789f14a6d340d2f", - "transactionIndex": "0x0", - "logIndex": "0x1", + "data": "0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000a000000000a00000000000014000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000004e756d626572000000000000000000000000000000000000000000000000000000000000000000000000000000000060000401000400000000000000000000000000000000000000000000000000000000040100030000000000000000000000000000000000000000000000000000000004010003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000036b65790000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000576616c7565000000000000000000000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0x63c9c3f365b40de92c07c06e86f096433bfb2e968c3caec07df00fa2cd1a46d1", + "transactionIndex": "0x1", + "logIndex": "0x26", "transactionLogIndex": "0x1", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0xd01f9f1368f831528fc9fe6442366b2b7d957fbfff3bcf7c24d9ab5fe51f8c46" + "0xe7b614a59a30dff5ea0536e50c4019312e856b636126e1b2d19a7c2872798735", + "0x000000000000000000000000000000005265736f757263655479706500000000" ], - "data": "0x000000000000000000000000000000005265736f7572636554797065000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000437573746f6d4572726f72735379737400000000000000000000000000000000000000000000000000000000000000010300000000000000000000000000000000000000000000000000000000000000", - "blockHash": "0x40b6d1b700f1f3dc080ebe2d54c3553e6419da61cfeb5a5e2e2272ebe9840b9a", - "blockNumber": "0x3", - "transactionHash": "0xb5f9defc11305940c4b08a6f34cdb4a671fde33905f288048b70c85b1d0875eb", - "transactionIndex": "0x1", - "logIndex": "0x2", + "data": "0x00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000566563746f720000000000000000000000000000000000000000000000000000000000000000000000000000000000010200000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0xe1f1a922f753965be316f50e3c51c91c436aff6091710ba37cd2c2b932625a6d", + "transactionIndex": "0x2", + "logIndex": "0x27", "transactionLogIndex": "0x0", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0x912af873e852235aae78a1d25ae9bb28b616a67c36898c53a14fd8184504ee32" + "0xc851f86da4b2f7d69c86fc8dc136b05aa9805e49c225918a8856af392597a34f", + "0x6d756473746f726500000000000000005461626c657300000000000000000000" ], - "data": "0x0000000000000000000000000000000053797374656d73000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000437573746f6d4572726f72735379737400000000000000000000000000000000000000000000000000000000000000155fc8d32690cc91d4c39d9d3abcbd16989f875707010000000000000000000000", - "blockHash": "0x40b6d1b700f1f3dc080ebe2d54c3553e6419da61cfeb5a5e2e2272ebe9840b9a", - "blockNumber": "0x3", - "transactionHash": "0xb5f9defc11305940c4b08a6f34cdb4a671fde33905f288048b70c85b1d0875eb", - "transactionIndex": "0x1", - "logIndex": "0x3", + "data": "0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000010000000000a0000000000001a00000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000566563746f7200000000000000000000000000000000000000000000000000000000000000000000000000000000006000080200040400000000000000000000000000000000000000000000000000000004010003000000000000000000000000000000000000000000000000000000000802002323000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000036b6579000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001780000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000017900000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0xe1f1a922f753965be316f50e3c51c91c436aff6091710ba37cd2c2b932625a6d", + "transactionIndex": "0x2", + "logIndex": "0x28", "transactionLogIndex": "0x1", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0xd01f9f1368f831528fc9fe6442366b2b7d957fbfff3bcf7c24d9ab5fe51f8c46" + "0xe7b614a59a30dff5ea0536e50c4019312e856b636126e1b2d19a7c2872798735", + "0x000000000000000000000000000000005265736f757263655479706500000000" ], - "data": "0x0000000000000000000000000000000053797374656d526567697374727900000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000010000000000000000000000005fc8d32690cc91d4c39d9d3abcbd16989f875707000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000437573746f6d4572726f727353797374", - "blockHash": "0x40b6d1b700f1f3dc080ebe2d54c3553e6419da61cfeb5a5e2e2272ebe9840b9a", - "blockNumber": "0x3", - "transactionHash": "0xb5f9defc11305940c4b08a6f34cdb4a671fde33905f288048b70c85b1d0875eb", - "transactionIndex": "0x1", - "logIndex": "0x4", - "transactionLogIndex": "0x2", + "data": "0x00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000004e756d6265724c69737400000000000000000000000000000000000000000000000000000000000000000000000000010200000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0x2a33d478b71e83b3075ae06f08da8260132fc8c0f8bb4e316b7c8c36b37a1fb8", + "transactionIndex": "0x3", + "logIndex": "0x29", + "transactionLogIndex": "0x0", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0xd01f9f1368f831528fc9fe6442366b2b7d957fbfff3bcf7c24d9ab5fe51f8c46" + "0xc851f86da4b2f7d69c86fc8dc136b05aa9805e49c225918a8856af392597a34f", + "0x6d756473746f726500000000000000005461626c657300000000000000000000" ], - "data": "0x000000000000000000000000000000005265736f7572636541636365737300000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000005fc8d32690cc91d4c39d9d3abcbd16989f87570700000000000000000000000000000000000000000000000000000000000000010100000000000000000000000000000000000000000000000000000000000000", - "blockHash": "0x40b6d1b700f1f3dc080ebe2d54c3553e6419da61cfeb5a5e2e2272ebe9840b9a", - "blockNumber": "0x3", - "transactionHash": "0xb5f9defc11305940c4b08a6f34cdb4a671fde33905f288048b70c85b1d0875eb", - "transactionIndex": "0x1", - "logIndex": "0x5", - "transactionLogIndex": "0x3", + "data": "0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000a00000000040000000000000e000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000004e756d6265724c697374000000000000000000000000000000000000000000000000000000000000000000000000006000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000576616c7565000000000000000000000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0x2a33d478b71e83b3075ae06f08da8260132fc8c0f8bb4e316b7c8c36b37a1fb8", + "transactionIndex": "0x3", + "logIndex": "0x2a", + "transactionLogIndex": "0x1", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0xd01f9f1368f831528fc9fe6442366b2b7d957fbfff3bcf7c24d9ab5fe51f8c46" + "0xe7b614a59a30dff5ea0536e50c4019312e856b636126e1b2d19a7c2872798735", + "0x000000000000000000000000000000005265736f757263655479706500000000" ], - "data": "0x000000000000000000000000000000005265736f7572636554797065000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000004e756d6265724c69737453797374656d00000000000000000000000000000000000000000000000000000000000000010300000000000000000000000000000000000000000000000000000000000000", - "blockHash": "0x40b6d1b700f1f3dc080ebe2d54c3553e6419da61cfeb5a5e2e2272ebe9840b9a", - "blockNumber": "0x3", - "transactionHash": "0x5e56042175cee9139c0439cc0df13d0efbdb1a57e93e5353f8fc713ea061bfce", - "transactionIndex": "0x2", - "logIndex": "0x6", + "data": "0x00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000004d756c7469000000000000000000000000000000000000000000000000000000000000000000000000000000000000010200000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0x7f5154e28a5640217c214a2007641ccb776e5b9609101919e28aead1d8eb6352", + "transactionIndex": "0x4", + "logIndex": "0x2b", "transactionLogIndex": "0x0", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0x912af873e852235aae78a1d25ae9bb28b616a67c36898c53a14fd8184504ee32" + "0xc851f86da4b2f7d69c86fc8dc136b05aa9805e49c225918a8856af392597a34f", + "0x6d756473746f726500000000000000005461626c657300000000000000000000" ], - "data": "0x0000000000000000000000000000000053797374656d73000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000004e756d6265724c69737453797374656d00000000000000000000000000000000000000000000000000000000000000150165878a594ca255338adfa4d48449f69242eb8f010000000000000000000000", - "blockHash": "0x40b6d1b700f1f3dc080ebe2d54c3553e6419da61cfeb5a5e2e2272ebe9840b9a", - "blockNumber": "0x3", - "transactionHash": "0x5e56042175cee9139c0439cc0df13d0efbdb1a57e93e5353f8fc713ea061bfce", - "transactionIndex": "0x2", - "logIndex": "0x7", + "data": "0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000010000000001c0000000000002c000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000004d756c74690000000000000000000000000000000000000000000000000000000000000000000000000000000000006000210200200100000000000000000000000000000000000000000000000000000034040003601f2e000000000000000000000000000000000000000000000000002102003f60000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000016100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000162000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001630000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000036e756d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000576616c7565000000000000000000000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0x7f5154e28a5640217c214a2007641ccb776e5b9609101919e28aead1d8eb6352", + "transactionIndex": "0x4", + "logIndex": "0x2c", "transactionLogIndex": "0x1", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0xd01f9f1368f831528fc9fe6442366b2b7d957fbfff3bcf7c24d9ab5fe51f8c46" + "0xe7b614a59a30dff5ea0536e50c4019312e856b636126e1b2d19a7c2872798735", + "0x000000000000000000000000000000005265736f757263655479706500000000" ], - "data": "0x0000000000000000000000000000000053797374656d526567697374727900000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000165878a594ca255338adfa4d48449f69242eb8f0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000004e756d6265724c69737453797374656d", - "blockHash": "0x40b6d1b700f1f3dc080ebe2d54c3553e6419da61cfeb5a5e2e2272ebe9840b9a", - "blockNumber": "0x3", - "transactionHash": "0x5e56042175cee9139c0439cc0df13d0efbdb1a57e93e5353f8fc713ea061bfce", - "transactionIndex": "0x2", - "logIndex": "0x8", - "transactionLogIndex": "0x2", + "data": "0x00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000437573746f6d4572726f72735379737400000000000000000000000000000000000000000000000000000000000000010300000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0x9522e814b8b7aa55524257cfb693c2fdcb6fe1afd7aae7f0daf68c426afcbac9", + "transactionIndex": "0x5", + "logIndex": "0x2d", + "transactionLogIndex": "0x0", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0xd01f9f1368f831528fc9fe6442366b2b7d957fbfff3bcf7c24d9ab5fe51f8c46" + "0xc851f86da4b2f7d69c86fc8dc136b05aa9805e49c225918a8856af392597a34f", + "0x0000000000000000000000000000000053797374656d73000000000000000000" ], - "data": "0x000000000000000000000000000000005265736f7572636541636365737300000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000165878a594ca255338adfa4d48449f69242eb8f00000000000000000000000000000000000000000000000000000000000000010100000000000000000000000000000000000000000000000000000000000000", - "blockHash": "0x40b6d1b700f1f3dc080ebe2d54c3553e6419da61cfeb5a5e2e2272ebe9840b9a", - "blockNumber": "0x3", - "transactionHash": "0x5e56042175cee9139c0439cc0df13d0efbdb1a57e93e5353f8fc713ea061bfce", - "transactionIndex": "0x2", - "logIndex": "0x9", - "transactionLogIndex": "0x3", + "data": "0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000437573746f6d4572726f72735379737400000000000000000000000000000000000000000000000000000000000000155fc8d32690cc91d4c39d9d3abcbd16989f8757070100000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0x9522e814b8b7aa55524257cfb693c2fdcb6fe1afd7aae7f0daf68c426afcbac9", + "transactionIndex": "0x5", + "logIndex": "0x2e", + "transactionLogIndex": "0x1", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0xd01f9f1368f831528fc9fe6442366b2b7d957fbfff3bcf7c24d9ab5fe51f8c46" + "0xe7b614a59a30dff5ea0536e50c4019312e856b636126e1b2d19a7c2872798735", + "0x0000000000000000000000000000000053797374656d52656769737472790000" ], - "data": "0x000000000000000000000000000000005265736f7572636554797065000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000566563746f720000000000000000000000000000000000000000000000000000000000000000000000000000000000010200000000000000000000000000000000000000000000000000000000000000", - "blockHash": "0x40b6d1b700f1f3dc080ebe2d54c3553e6419da61cfeb5a5e2e2272ebe9840b9a", - "blockNumber": "0x3", - "transactionHash": "0xb3febd63ed51098bd7062390e4e859508e55a1bbfa5ee8ec197a4df93586f632", - "transactionIndex": "0x3", - "logIndex": "0xa", - "transactionLogIndex": "0x0", + "data": "0x00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000010000000000000000000000005fc8d32690cc91d4c39d9d3abcbd16989f875707000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000437573746f6d4572726f727353797374", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0x9522e814b8b7aa55524257cfb693c2fdcb6fe1afd7aae7f0daf68c426afcbac9", + "transactionIndex": "0x5", + "logIndex": "0x2f", + "transactionLogIndex": "0x2", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0x912af873e852235aae78a1d25ae9bb28b616a67c36898c53a14fd8184504ee32" + "0xe7b614a59a30dff5ea0536e50c4019312e856b636126e1b2d19a7c2872798735", + "0x000000000000000000000000000000005265736f757263654163636573730000" ], - "data": "0x6d756473746f726500000000000000005461626c657300000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000566563746f72000000000000000000000000000000000000000000000000000000000000000000000000000000000220000802000404000000000000000000000000000000000000000000000000000000040100030000000000000000000000000000000000000000000000000000000008020023230000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000a0000000000001a000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000036b6579000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001780000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000017900000000000000000000000000000000000000000000000000000000000000", - "blockHash": "0x40b6d1b700f1f3dc080ebe2d54c3553e6419da61cfeb5a5e2e2272ebe9840b9a", - "blockNumber": "0x3", - "transactionHash": "0xb3febd63ed51098bd7062390e4e859508e55a1bbfa5ee8ec197a4df93586f632", - "transactionIndex": "0x3", - "logIndex": "0xb", - "transactionLogIndex": "0x1", + "data": "0x00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000005fc8d32690cc91d4c39d9d3abcbd16989f87570700000000000000000000000000000000000000000000000000000000000000010100000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0x9522e814b8b7aa55524257cfb693c2fdcb6fe1afd7aae7f0daf68c426afcbac9", + "transactionIndex": "0x5", + "logIndex": "0x30", + "transactionLogIndex": "0x3", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0xd01f9f1368f831528fc9fe6442366b2b7d957fbfff3bcf7c24d9ab5fe51f8c46" + "0xe7b614a59a30dff5ea0536e50c4019312e856b636126e1b2d19a7c2872798735", + "0x000000000000000000000000000000005265736f757263655479706500000000" ], - "data": "0x000000000000000000000000000000005265736f7572636554797065000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000004e756d6265720000000000000000000000000000000000000000000000000000000000000000000000000000000000010200000000000000000000000000000000000000000000000000000000000000", - "blockHash": "0x40b6d1b700f1f3dc080ebe2d54c3553e6419da61cfeb5a5e2e2272ebe9840b9a", - "blockNumber": "0x3", - "transactionHash": "0xd9aea844188d36cd599b3d9ddd523b1269131ec10ebf5b107a50bffe8a03985e", - "transactionIndex": "0x4", - "logIndex": "0xc", + "data": "0x00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000004e756d6265724c69737453797374656d00000000000000000000000000000000000000000000000000000000000000010300000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0x21d2bdc995fad3ad12bc649405f9c25522526bf04a2ba379fd5cb4056e9368f3", + "transactionIndex": "0x6", + "logIndex": "0x31", "transactionLogIndex": "0x0", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0x912af873e852235aae78a1d25ae9bb28b616a67c36898c53a14fd8184504ee32" + "0xc851f86da4b2f7d69c86fc8dc136b05aa9805e49c225918a8856af392597a34f", + "0x0000000000000000000000000000000053797374656d73000000000000000000" ], - "data": "0x6d756473746f726500000000000000005461626c657300000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000004e756d6265720000000000000000000000000000000000000000000000000000000000000000000000000000000001c000040100040000000000000000000000000000000000000000000000000000000004010003000000000000000000000000000000000000000000000000000000000401000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000a00000000000014000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000036b65790000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000576616c7565000000000000000000000000000000000000000000000000000000", - "blockHash": "0x40b6d1b700f1f3dc080ebe2d54c3553e6419da61cfeb5a5e2e2272ebe9840b9a", - "blockNumber": "0x3", - "transactionHash": "0xd9aea844188d36cd599b3d9ddd523b1269131ec10ebf5b107a50bffe8a03985e", - "transactionIndex": "0x4", - "logIndex": "0xd", + "data": "0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000004e756d6265724c69737453797374656d00000000000000000000000000000000000000000000000000000000000000150165878a594ca255338adfa4d48449f69242eb8f0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0x21d2bdc995fad3ad12bc649405f9c25522526bf04a2ba379fd5cb4056e9368f3", + "transactionIndex": "0x6", + "logIndex": "0x32", "transactionLogIndex": "0x1", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0xd01f9f1368f831528fc9fe6442366b2b7d957fbfff3bcf7c24d9ab5fe51f8c46" + "0xe7b614a59a30dff5ea0536e50c4019312e856b636126e1b2d19a7c2872798735", + "0x0000000000000000000000000000000053797374656d52656769737472790000" ], - "data": "0x000000000000000000000000000000005265736f7572636554797065000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000004d756c7469000000000000000000000000000000000000000000000000000000000000000000000000000000000000010200000000000000000000000000000000000000000000000000000000000000", - "blockHash": "0x40b6d1b700f1f3dc080ebe2d54c3553e6419da61cfeb5a5e2e2272ebe9840b9a", - "blockNumber": "0x3", - "transactionHash": "0xa50c88a503c25f17dc6c8e4cc779f15341f6f8242da501989a64ec87a8f5a85b", - "transactionIndex": "0x5", - "logIndex": "0xe", - "transactionLogIndex": "0x0", + "data": "0x00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000165878a594ca255338adfa4d48449f69242eb8f0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000004e756d6265724c69737453797374656d", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0x21d2bdc995fad3ad12bc649405f9c25522526bf04a2ba379fd5cb4056e9368f3", + "transactionIndex": "0x6", + "logIndex": "0x33", + "transactionLogIndex": "0x2", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0x912af873e852235aae78a1d25ae9bb28b616a67c36898c53a14fd8184504ee32" + "0xe7b614a59a30dff5ea0536e50c4019312e856b636126e1b2d19a7c2872798735", + "0x000000000000000000000000000000005265736f757263654163636573730000" ], - "data": "0x6d756473746f726500000000000000005461626c657300000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000004d756c74690000000000000000000000000000000000000000000000000000000000000000000000000000000000034000210200200100000000000000000000000000000000000000000000000000000034040003601f2e000000000000000000000000000000000000000000000000002102003f600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000001c0000000000002c000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000016100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000162000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001630000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000036e756d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000576616c7565000000000000000000000000000000000000000000000000000000", - "blockHash": "0x40b6d1b700f1f3dc080ebe2d54c3553e6419da61cfeb5a5e2e2272ebe9840b9a", - "blockNumber": "0x3", - "transactionHash": "0xa50c88a503c25f17dc6c8e4cc779f15341f6f8242da501989a64ec87a8f5a85b", - "transactionIndex": "0x5", - "logIndex": "0xf", - "transactionLogIndex": "0x1", + "data": "0x00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000165878a594ca255338adfa4d48449f69242eb8f00000000000000000000000000000000000000000000000000000000000000010100000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0x21d2bdc995fad3ad12bc649405f9c25522526bf04a2ba379fd5cb4056e9368f3", + "transactionIndex": "0x6", + "logIndex": "0x34", + "transactionLogIndex": "0x3", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0x912af873e852235aae78a1d25ae9bb28b616a67c36898c53a14fd8184504ee32" + "0xc851f86da4b2f7d69c86fc8dc136b05aa9805e49c225918a8856af392597a34f", + "0x0000000000000000000000000000000046756e6374696f6e53656c6563746f72" ], - "data": "0x0000000000000000000000000000000046756e6374696f6e53656c6563746f72000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000015f644e3c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000437573746f6d4572726f7273537973745f644e3c00000000000000000000000000000000000000000000000000000000", - "blockHash": "0x40b6d1b700f1f3dc080ebe2d54c3553e6419da61cfeb5a5e2e2272ebe9840b9a", - "blockNumber": "0x3", - "transactionHash": "0xff27259bf2a15d32d629b5f2dce0f7b248400c760cd4a4f367670e87b4cf81fb", - "transactionIndex": "0x6", - "logIndex": "0x10", + "data": "0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000015f644e3c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000437573746f6d4572726f7273537973745f644e3c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0xa7b13684919b9c2f639041255d439afd3468db01f44f2699e159704399a11d5c", + "transactionIndex": "0x7", + "logIndex": "0x35", "transactionLogIndex": "0x0", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0x912af873e852235aae78a1d25ae9bb28b616a67c36898c53a14fd8184504ee32" + "0xc851f86da4b2f7d69c86fc8dc136b05aa9805e49c225918a8856af392597a34f", + "0x0000000000000000000000000000000046756e6374696f6e53656c6563746f72" ], - "data": "0x0000000000000000000000000000000046756e6374696f6e53656c6563746f72000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001b8a44c7c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000004e756d6265724c69737453797374656db8a44c7c00000000000000000000000000000000000000000000000000000000", - "blockHash": "0x40b6d1b700f1f3dc080ebe2d54c3553e6419da61cfeb5a5e2e2272ebe9840b9a", - "blockNumber": "0x3", - "transactionHash": "0x704d3a95376db47cabae3ba83743e46e1eb751b82a9b622af5a555e109d59a66", - "transactionIndex": "0x7", - "logIndex": "0x11", + "data": "0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000001a4ece52c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000004e756d6265724c69737453797374656da4ece52c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0xd06d889514df379529e4231e85b2d241559dc810b765152c17047ae767b51cf0", + "transactionIndex": "0x8", + "logIndex": "0x36", "transactionLogIndex": "0x0", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0x912af873e852235aae78a1d25ae9bb28b616a67c36898c53a14fd8184504ee32" + "0xc851f86da4b2f7d69c86fc8dc136b05aa9805e49c225918a8856af392597a34f", + "0x0000000000000000000000000000000046756e6374696f6e53656c6563746f72" ], - "data": "0x0000000000000000000000000000000046756e6374696f6e53656c6563746f72000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a4ece52c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000004e756d6265724c69737453797374656da4ece52c00000000000000000000000000000000000000000000000000000000", - "blockHash": "0x40b6d1b700f1f3dc080ebe2d54c3553e6419da61cfeb5a5e2e2272ebe9840b9a", - "blockNumber": "0x3", - "transactionHash": "0x9d7756b56cd32ba0a3305788a5c412eae328b0a28c4ff2e46f5db939a1190a77", - "transactionIndex": "0x8", - "logIndex": "0x12", + "data": "0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000001f7f0e440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000004e756d6265724c69737453797374656df7f0e440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0x08304f1f19f128d652cf1f5b599e1c8b87e5b8164e3046e7041aabc886b7227f", + "transactionIndex": "0x9", + "logIndex": "0x37", "transactionLogIndex": "0x0", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0x912af873e852235aae78a1d25ae9bb28b616a67c36898c53a14fd8184504ee32" + "0xc851f86da4b2f7d69c86fc8dc136b05aa9805e49c225918a8856af392597a34f", + "0x0000000000000000000000000000000046756e6374696f6e53656c6563746f72" ], - "data": "0x0000000000000000000000000000000046756e6374696f6e53656c6563746f72000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001f7f0e440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000004e756d6265724c69737453797374656df7f0e44000000000000000000000000000000000000000000000000000000000", - "blockHash": "0x40b6d1b700f1f3dc080ebe2d54c3553e6419da61cfeb5a5e2e2272ebe9840b9a", - "blockNumber": "0x3", - "transactionHash": "0x8a7d4a27b5a121f77b0011d084850b4502db7cc7e30f5a22e1fdf61247eac74c", - "transactionIndex": "0x9", - "logIndex": "0x13", + "data": "0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000001306d61a5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000004e756d6265724c69737453797374656d306d61a5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0xab1ec3ac7ebed007e57666d5fbb7c5ba174c9695ea87d9cd5025f1323d5cd573", + "transactionIndex": "0xa", + "logIndex": "0x38", "transactionLogIndex": "0x0", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0x912af873e852235aae78a1d25ae9bb28b616a67c36898c53a14fd8184504ee32" + "0xc851f86da4b2f7d69c86fc8dc136b05aa9805e49c225918a8856af392597a34f", + "0x0000000000000000000000000000000046756e6374696f6e53656c6563746f72" ], - "data": "0x0000000000000000000000000000000046756e6374696f6e53656c6563746f72000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001306d61a5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000004e756d6265724c69737453797374656d306d61a500000000000000000000000000000000000000000000000000000000", - "blockHash": "0x40b6d1b700f1f3dc080ebe2d54c3553e6419da61cfeb5a5e2e2272ebe9840b9a", - "blockNumber": "0x3", - "transactionHash": "0xf97efde38e1aaee38023ff6669c33b26ac5230f364af583b6159dfec83099c02", - "transactionIndex": "0xa", - "logIndex": "0x14", + "data": "0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000001b8a44c7c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000004e756d6265724c69737453797374656db8a44c7c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x26210b5307d8f2e8f7ce8c759d3563b48fad64b1021b2fb6100d83cd7f55a3cd", + "blockNumber": "0x4", + "transactionHash": "0xef2202fbaac567d90f4790707035892d554dc226c0d481ffe667f1818774ad7b", + "transactionIndex": "0xb", + "logIndex": "0x39", "transactionLogIndex": "0x0", "removed": false }, { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0xd01f9f1368f831528fc9fe6442366b2b7d957fbfff3bcf7c24d9ab5fe51f8c46" + "0x5599f2052ade9808af7738adc73853c23894a2f1e944c827f7f6b8e948c4e885", + "0x000000000000000000000000000000004e756d6265724c697374000000000000" ], - "data": "0x000000000000000000000000000000004e756d6265724c6973740000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000001a400000000000000000000000000000000000000000000000000000000", - "blockHash": "0xcc9c86d971dbe128cf77e6c94d85c369dc31f6a9b0b75324cc5feb5215a4b045", - "blockNumber": "0x4", - "transactionHash": "0x6b11c4b4d9257da889cd755a9aef98068339dea0770f3c43def5293c47977876", + "data": "0x00000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000040000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000001a400000000000000000000000000000000000000000000000000000000", + "blockHash": "0xf77983f832702b99ab5712e6c94615f2125f82e1b279f966c49fd8c8d8bd03f1", + "blockNumber": "0x5", + "transactionHash": "0xe373575d25699927fbfcd0fb9eb2b48ebaed83b87d3ca1e0a30478ac2ae2be4f", "transactionIndex": "0x0", "logIndex": "0x0", "transactionLogIndex": "0x0", @@ -828,12 +887,13 @@ { "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "topics": [ - "0xd01f9f1368f831528fc9fe6442366b2b7d957fbfff3bcf7c24d9ab5fe51f8c46" + "0x5599f2052ade9808af7738adc73853c23894a2f1e944c827f7f6b8e948c4e885", + "0x000000000000000000000000000000004e756d6265724c697374000000000000" ], - "data": "0x000000000000000000000000000000004e756d6265724c6973740000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000001a400000045000000000000000000000000000000000000000000000000", - "blockHash": "0xcc9c86d971dbe128cf77e6c94d85c369dc31f6a9b0b75324cc5feb5215a4b045", - "blockNumber": "0x4", - "transactionHash": "0x2453e912dfab0eed54e6140d2f5c829034fe51fa844767991510eb71ecff8a3b", + "data": "0x00000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000800000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000004500000000000000000000000000000000000000000000000000000000", + "blockHash": "0xf77983f832702b99ab5712e6c94615f2125f82e1b279f966c49fd8c8d8bd03f1", + "blockNumber": "0x5", + "transactionHash": "0xfec9e0db2f68f31109c2f8fb678f398807407b88deb1b9644f1638e0012fac7c", "transactionIndex": "0x1", "logIndex": "0x1", "transactionLogIndex": "0x0",