From 08b6a064f8ff91fe43af754bee9748ef835964f3 Mon Sep 17 00:00:00 2001 From: alvrs Date: Tue, 17 Jan 2023 22:47:25 +0100 Subject: [PATCH 01/82] feat(solecs): wip prototype for new data model core library --- packages/solecs/foundry.toml | 2 +- packages/solecs/v2/Bytes.sol | 97 ++++++++++++ packages/solecs/v2/IStore.sol | 7 + packages/solecs/v2/StoreCore.sol | 191 ++++++++++++++++++++++++ packages/solecs/v2/Types.sol | 9 ++ packages/solecs/v2/Utils.sol | 8 + packages/solecs/v2/test/Bytes.t.sol | 131 ++++++++++++++++ packages/solecs/v2/test/StoreCore.t.sol | 86 +++++++++++ 8 files changed, 530 insertions(+), 1 deletion(-) create mode 100644 packages/solecs/v2/Bytes.sol create mode 100644 packages/solecs/v2/IStore.sol create mode 100644 packages/solecs/v2/StoreCore.sol create mode 100644 packages/solecs/v2/Types.sol create mode 100644 packages/solecs/v2/Utils.sol create mode 100644 packages/solecs/v2/test/Bytes.t.sol create mode 100644 packages/solecs/v2/test/StoreCore.t.sol diff --git a/packages/solecs/foundry.toml b/packages/solecs/foundry.toml index 4e1a99ebba..19294352cb 100644 --- a/packages/solecs/foundry.toml +++ b/packages/solecs/foundry.toml @@ -5,5 +5,5 @@ optimizer = true optimizer_runs = 1000000 verbosity = 1 libs = ["../../node_modules"] -src = "src" +src = "v2" out = "out" \ No newline at end of file diff --git a/packages/solecs/v2/Bytes.sol b/packages/solecs/v2/Bytes.sol new file mode 100644 index 0000000000..cbe521f776 --- /dev/null +++ b/packages/solecs/v2/Bytes.sol @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import { Utils } from "./Utils.sol"; +import { SchemaType } from "./Types.sol"; + +library Bytes { + error Bytes_InputTooShort(); + + /** + * Converts a `bytes` memory array to a single `bytes` memory value. + * TODO: optimize gas cost + */ + function from(bytes[] memory input) internal pure returns (bytes memory output) { + output = new bytes(0); + for (uint256 i; i < input.length; i++) { + output = bytes.concat(output, input[i]); + } + } + + function from(uint8[] memory input) internal pure returns (bytes memory output) { + output = new bytes(input.length); + for (uint256 i; i < input.length; i++) { + output[i] = bytes1(input[i]); + } + } + + function from(SchemaType[] memory input) internal pure returns (bytes memory output) { + output = new bytes(input.length); + for (uint256 i; i < input.length; i++) { + output[i] = bytes1(uint8(input[i])); + } + } + + /** + * Converts a `bytes` memory blob to a single `bytes32` memory value, starting at the given byte offset. + */ + function toBytes32(bytes memory input, uint256 offset) internal pure returns (bytes32 output) { + assembly { + // input is a pointer to the start of the bytes array + // in memory, the first 32 bytes are the length of the array + // so we add 32 to the pointer to get to the start of the data + // then we add the start offset to get to the start of the desired word + output := mload(add(input, add(0x20, offset))) + } + } + + /** + * Converts a `bytes` memory blob to a single `bytes32` memory value. + */ + function toBytes32(bytes memory input) internal pure returns (bytes32 output) { + return toBytes32(input, 0); + } + + /** + * Converts a `bytes` memory blob to a single `uint256` memory value. + */ + function toUint256(bytes memory input) internal pure returns (uint256 output) { + return uint256(toBytes32(input)); + } + + /** + * Converts a `bytes` memory blob to a `bytes32` memory array. + */ + function toBytes32Array(bytes memory input) internal pure returns (bytes32[] memory output) { + output = new bytes32[](Utils.divCeil(input.length, 32)); + for (uint256 i = 0; i < output.length; i++) { + output[i] = toBytes32(input, i * 32); + } + return output; + } + + /** + * Converts a `bytes` memory blob to a `SchemaType` memory array. + */ + function toSchemaTypeArray(bytes memory input) internal pure returns (SchemaType[] memory output) { + output = new SchemaType[](input.length); + for (uint256 i = 0; i < output.length; i++) { + output[i] = SchemaType(uint8(input[i])); + } + return output; + } + + function equals(bytes memory a, bytes memory b) internal pure returns (bool) { + if (a.length != b.length) { + return false; + } + return keccak256(a) == keccak256(b); + } + + function setLengthInPlace(bytes memory input, uint256 length) internal pure returns (bytes memory) { + assembly { + mstore(input, length) + } + return input; + } +} diff --git a/packages/solecs/v2/IStore.sol b/packages/solecs/v2/IStore.sol new file mode 100644 index 0000000000..2ce43a8a4b --- /dev/null +++ b/packages/solecs/v2/IStore.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +interface IStore { + // note: the preimage of the tuple of keys used to index is part of the event, so it can be used by indexers + event StoreUpdate(bytes32 table, bytes32[] key, uint8 schemaIndex, bytes[] data); +} diff --git a/packages/solecs/v2/StoreCore.sol b/packages/solecs/v2/StoreCore.sol new file mode 100644 index 0000000000..7f7ebf7a92 --- /dev/null +++ b/packages/solecs/v2/StoreCore.sol @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; +import { Utils } from "./Utils.sol"; +import { Bytes } from "./Bytes.sol"; +import { SchemaType } from "./Types.sol"; +import { console } from "forge-std/console.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 StoreUpdate(bytes32 table, bytes32[] key, uint8 schemaIndex, bytes[] data); + bytes32 constant _slot = keccak256("mud.store"); + bytes32 constant _schemaTable = keccak256("mud.store.table.schema"); + + error StoreCore_SchemaTooLong(); + + /** + * Compute the storage location based on table id and index tuple + * TODO: provide different overloads for single key and some fixed length array keys for better devex + */ + function _getLocation(bytes32 table, bytes32[] memory key) internal pure returns (bytes32) { + return keccak256(abi.encode(_slot, table, key)); + } + + function _getLocation(bytes32 table, bytes32 key) internal pure returns (bytes32) { + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = key; + return _getLocation(table, keyTuple); + } + + /** + * Check if the given table exists + */ + function hasTable(bytes32 table) internal view returns (bool) { + // TODO + } + + /** + * Register a new table schema + */ + function registerSchema(bytes32 table, SchemaType[] memory schema) internal { + // TODO: verify the table doesn't already exist + if (schema.length > 32) revert StoreCore_SchemaTooLong(); + bytes32 location = _getLocation(_schemaTable, table); + _setDataRaw(location, Bytes.from(schema)); + } + + /** + * Get the schema for the given table + */ + function getSchema(bytes32 table) internal view returns (SchemaType[] memory schema) { + bytes32 location = _getLocation(_schemaTable, table); + bytes memory blob = _getDataRaw(location, 32); + + // Find the first `None` value in the schema to determine the length + uint256 length = 0; + while (length < 32 && blob[length] != bytes1(uint8(SchemaType.None))) { + length++; + } + + // Decrease the blob size to the actual length + Bytes.setLengthInPlace(blob, length); + schema = Bytes.toSchemaTypeArray(blob); + } + + /** + * Set data for the given table and key tuple + */ + function setData( + bytes32 table, + bytes32[] memory key, + bytes[] memory data + ) internal { + // TODO: verify the value has the correct length for the table (based on the table's schema) + // (Tradeoff, slightly higher cost due to additional sload, but higher security - library could also provide both options) + + // Store the provided value in storage + bytes32 location = _getLocation(table, key); + bytes memory blob = Bytes.from(data); + _setDataRaw(location, blob); + + // Emit event to notify indexers + emit StoreUpdate(table, key, 0, data); + } + + /** + * Set data for the given table and single key + */ + function setData( + bytes32 table, + bytes32 key, + bytes[] memory data + ) internal { + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = key; + setData(table, keyTuple, data); + } + + /** + * Write raw bytes to storage at the given location + */ + function _setDataRaw(bytes32 location, bytes memory data) internal { + assembly { + // loop over data and sstore it, starting at `location` + // (don't store length, since it is known from the schema) + for { + let i := 0 + } lt(i, mload(data)) { + i := add(i, 0x20) // increment by 32 since we are storing 32 bytes at a time + } { + sstore(add(location, i), mload(add(data, add(0x20, i)))) + } + } + } + + /** + * Get full data for the given table and key tuple + */ + function getData(bytes32 table, bytes32[] memory key) internal view returns (bytes[] memory data) { + // Get schema for this table + SchemaType[] memory schema = getSchema(table); + + // Compute length of the full schema + uint256 length = _getByteLength(schema); + + // Load the data from storage + bytes32 location = _getLocation(table, key); + bytes memory blob = _getDataRaw(location, length); + + // Split up data into bytes[] based on schema + } + + /** + * Read raw bytes from storage at the given location and length in bytess + */ + function _getDataRaw(bytes32 location, uint256 length) internal view returns (bytes memory data) { + data = new bytes(length); + // load data from storage into memory + assembly { + for { + let i := 0 + } lt(i, length) { + i := add(i, 0x20) // increment by 32 since we are loading 32 bytes at a time + } { + mstore(add(data, add(0x20, i)), sload(add(location, i))) + } + } + } + + /** + * Get the length of the data for the given schema type + * (Because Solidity doesn't support constant arrays, we need to use a function) + */ + function _getByteLength(SchemaType schemaType) internal pure returns (uint256) { + if (schemaType == SchemaType.Uint8) { + return 1; + } else if (schemaType == SchemaType.Uint16) { + return 2; + } else { + revert("Unsupported schema type"); + } + } + + /** + * Get the length of the data for the given schema + */ + function _getByteLength(SchemaType[] memory schema) internal pure returns (uint256) { + uint256 length = 0; + for (uint256 i = 0; i < schema.length; i++) { + length += _getByteLength(schema[i]); + } + return length; + } + + /** + * Split the given blob into bytes[] based on the given schema + * TODO: change implementation work on bits instead of bytes + * TODO: gas golf + */ + function _split(bytes memory blob, SchemaType[] memory schema) internal pure returns (bytes[] memory output) { + output = new bytes[](schema.length); + uint256 offset; + for (uint256 i = 0; i < schema.length; i++) { + uint256 length = _getByteLength(schema[i]); + output[i] = new bytes(length); + for (uint256 j = 0; j < length; j++) { + output[i][j] = blob[offset + j]; + } + offset += length; + } + } +} diff --git a/packages/solecs/v2/Types.sol b/packages/solecs/v2/Types.sol new file mode 100644 index 0000000000..cf38a66f1e --- /dev/null +++ b/packages/solecs/v2/Types.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +// TODO: add remaining types +enum SchemaType { + None, // The first `None` value ends the schema + Uint8, + Uint16 +} diff --git a/packages/solecs/v2/Utils.sol b/packages/solecs/v2/Utils.sol new file mode 100644 index 0000000000..d896c5e58c --- /dev/null +++ b/packages/solecs/v2/Utils.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +library Utils { + function divCeil(uint256 a, uint256 b) internal pure returns (uint256) { + return a / b + (a % b == 0 ? 0 : 1); + } +} diff --git a/packages/solecs/v2/test/Bytes.t.sol b/packages/solecs/v2/test/Bytes.t.sol new file mode 100644 index 0000000000..293ffd4d69 --- /dev/null +++ b/packages/solecs/v2/test/Bytes.t.sol @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import { console } from "forge-std/console.sol"; +import { DSTestPlus } from "solmate/test/utils/DSTestPlus.sol"; +import { Bytes } from "../Bytes.sol"; + +contract BytesTest is DSTestPlus { + function testFromBytesArray() public { + bytes[] memory input = new bytes[](2); + input[0] = new bytes(32); + input[0][0] = 0x01; + input[0][31] = 0x02; + input[1] = new bytes(32); + input[1][0] = 0x03; + input[1][31] = 0x04; + uint256 gas = gasleft(); + bytes memory output = Bytes.from(input); + gas = gas - gasleft(); + console.log("gas used: %s", gas); + assertEq(output.length, 64); + assertEq(uint256(Bytes.toBytes32(output, 0)), 0x0100000000000000000000000000000000000000000000000000000000000002); + assertEq(uint256(Bytes.toBytes32(output, 32)), 0x0300000000000000000000000000000000000000000000000000000000000004); + } + + function testFromUint8Array() public { + uint8[] memory input = new uint8[](2); + input[0] = 0x01; + input[1] = 0x02; + uint256 gas = gasleft(); + bytes memory output = Bytes.from(input); + gas = gas - gasleft(); + console.log("gas used: %s", gas); + assertEq(output.length, 2); + assertEq(uint256(uint8(output[0])), 0x01); + assertEq(uint256(uint8(output[1])), 0x02); + } + + function testToBytes32() public { + bytes memory input = new bytes(32); + input[0] = 0x01; + input[31] = 0x02; + uint256 gas = gasleft(); + bytes32 output = Bytes.toBytes32(input, 0); + gas = gas - gasleft(); + console.log("gas used: %s", gas); + assertEq(uint256(output), 0x0100000000000000000000000000000000000000000000000000000000000002); + } + + function testToBytes32CrossWord() public { + bytes memory input = new bytes(64); + input[0 + 16] = 0x01; + input[31 + 16] = 0x02; + uint256 gas = gasleft(); + bytes32 output = Bytes.toBytes32(input, 16); + gas = gas - gasleft(); + console.log("gas used: %s", gas); + assertEq(uint256(output), 0x0100000000000000000000000000000000000000000000000000000000000002); + } + + function testToBytesArray() public { + bytes memory input = new bytes(64); + input[0] = 0x01; + input[31] = 0x02; + input[32] = 0x03; + input[63] = 0x04; + uint256 gas = gasleft(); + bytes32[] memory output = Bytes.toBytes32Array(input); + gas = gas - gasleft(); + console.log("gas used: %s", gas); + assertEq(output.length, 2); + assertEq(uint256(output[0]), 0x0100000000000000000000000000000000000000000000000000000000000002); + assertEq(uint256(output[1]), 0x0300000000000000000000000000000000000000000000000000000000000004); + } + + function testToBytesArrayUneven() public { + bytes memory input = new bytes(65); + input[0] = 0x01; + input[31] = 0x02; + input[32] = 0x03; + input[63] = 0x04; + input[64] = 0x05; + uint256 gas = gasleft(); + bytes32[] memory output = Bytes.toBytes32Array(input); + gas = gas - gasleft(); + console.log("gas used: %s", gas); + assertEq(output.length, 3); + assertEq(uint256(output[0]), 0x0100000000000000000000000000000000000000000000000000000000000002); + assertEq(uint256(output[1]), 0x0300000000000000000000000000000000000000000000000000000000000004); + assertEq(uint256(output[2]), 0x0500000000000000000000000000000000000000000000000000000000000000); + } + + function testEquals() public { + bytes memory a = bytes("a"); + bytes memory b = bytes("a"); + uint256 gas = gasleft(); + assertTrue(Bytes.equals(a, b)); + gas = gas - gasleft(); + console.log("gas used: %s", gas); + } + + function testEqualsFalse() public { + bytes memory a = bytes("a"); + bytes memory b = bytes("b"); + uint256 gas = gasleft(); + assertFalse(Bytes.equals(a, b)); + gas = gas - gasleft(); + console.log("gas used: %s", gas); + } + + function testEqualsFalseDiffLength() public { + bytes memory a = bytes("a"); + bytes memory b = bytes("aa"); + uint256 gas = gasleft(); + assertFalse(Bytes.equals(a, b)); + gas = gas - gasleft(); + console.log("gas used: %s", gas); + } + + function testSetLengthInPlace() public { + bytes memory a = new bytes(5); + assertEq(a.length, 5); + + uint256 gas = gasleft(); + Bytes.setLengthInPlace(a, 2); + gas = gas - gasleft(); + console.log("gas used: %s", gas); + + assertEq(a.length, 2); + } +} diff --git a/packages/solecs/v2/test/StoreCore.t.sol b/packages/solecs/v2/test/StoreCore.t.sol new file mode 100644 index 0000000000..329f218f3f --- /dev/null +++ b/packages/solecs/v2/test/StoreCore.t.sol @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import { console } from "forge-std/console.sol"; +import { DSTestPlus } from "solmate/test/utils/DSTestPlus.sol"; +import { StoreCore } from "../StoreCore.sol"; +import { Utils } from "../Utils.sol"; +import { Bytes } from "../Bytes.sol"; +import { SchemaType } from "../Types.sol"; + +contract StoreCoreTest is DSTestPlus { + function testSetAndGetDataRawOneSlot() public { + bytes32 location = keccak256("some location"); + bytes memory data = new bytes(32); + + data[0] = 0x01; + data[31] = 0x02; + + uint256 gas = gasleft(); + StoreCore._setDataRaw(location, data); + gas = gas - gasleft(); + console.log("gas used (set): %s", gas); + + gas = gasleft(); + bytes memory loadedData = StoreCore._getDataRaw(location, data.length); + gas = gas - gasleft(); + console.log("gas used (get, warm): %s", gas); + + assertEq(bytes32(loadedData), bytes32(data)); + } + + function testSetAndGetDataRawMultipleSlots() public { + bytes32 location = keccak256("some location"); + bytes memory data = abi.encode("this is some data spanning multiple words"); + + uint256 gas = gasleft(); + StoreCore._setDataRaw(location, data); + gas = gas - gasleft(); + console.log("gas used (set, %s slots): %s", Utils.divCeil(data.length, 32), gas); + + gas = gasleft(); + bytes memory loadedData = StoreCore._getDataRaw(location, data.length); + gas = gas - gasleft(); + console.log("gas used (get, warm, %s slots): %s", Utils.divCeil(data.length, 32), gas); + + assertTrue(Bytes.equals(data, loadedData)); + } + + function testRegisterAndGetSchema() public { + SchemaType[] memory schema = new SchemaType[](4); + schema[0] = SchemaType.Uint8; + schema[1] = SchemaType.Uint16; + schema[2] = SchemaType.Uint8; + schema[3] = SchemaType.Uint16; + + bytes32 table = keccak256("some.table"); + uint256 gas = gasleft(); + StoreCore.registerSchema(table, schema); + gas = gas - gasleft(); + console.log("gas used (register): %s", gas); + + gas = gasleft(); + SchemaType[] memory loadedSchema = StoreCore.getSchema(table); + gas = gas - gasleft(); + console.log("gas used (get schema, warm): %s", gas); + + assertEq(loadedSchema.length, schema.length); + assertEq(uint8(schema[0]), uint8(loadedSchema[0])); + assertEq(uint8(schema[1]), uint8(loadedSchema[1])); + assertEq(uint8(schema[2]), uint8(loadedSchema[2])); + assertEq(uint8(schema[3]), uint8(loadedSchema[3])); + } + + function testSplit() public { + SchemaType[] memory schema = new SchemaType[](2); + schema[0] = SchemaType.Uint8; + schema[1] = SchemaType.Uint16; + + bytes memory data = bytes.concat(bytes1(0x01), bytes2(0x0203)); + bytes[] memory splitData = StoreCore._split(data, schema); + + assertEq(splitData.length, schema.length); + assertEq(uint8(bytes1(splitData[0])), 0x01); + assertEq(uint16(bytes2(splitData[1])), 0x0203); + } +} From 50009ffd44d76dd62cde841bac27ddb3374892b7 Mon Sep 17 00:00:00 2001 From: alvrs Date: Wed, 18 Jan 2023 12:20:18 +0100 Subject: [PATCH 02/82] feat(solecs): wip data model - add getData and split --- packages/solecs/v2/Bytes.sol | 40 +++++++ packages/solecs/v2/StoreCore.sol | 134 +++++++++++++++--------- packages/solecs/v2/test/StoreCore.t.sol | 49 +++++++++ 3 files changed, 175 insertions(+), 48 deletions(-) diff --git a/packages/solecs/v2/Bytes.sol b/packages/solecs/v2/Bytes.sol index cbe521f776..ea9cde1e56 100644 --- a/packages/solecs/v2/Bytes.sol +++ b/packages/solecs/v2/Bytes.sol @@ -7,6 +7,12 @@ import { SchemaType } from "./Types.sol"; library Bytes { error Bytes_InputTooShort(); + /************************************************************************ + * + * ANYTHING -> BYTES + * + ************************************************************************/ + /** * Converts a `bytes` memory array to a single `bytes` memory value. * TODO: optimize gas cost @@ -32,6 +38,12 @@ library Bytes { } } + /************************************************************************ + * + * BYTES -> ANYTHING + * + ************************************************************************/ + /** * Converts a `bytes` memory blob to a single `bytes32` memory value, starting at the given byte offset. */ @@ -81,6 +93,12 @@ library Bytes { return output; } + /************************************************************************ + * + * BYTES UTILS + * + ************************************************************************/ + function equals(bytes memory a, bytes memory b) internal pure returns (bool) { if (a.length != b.length) { return false; @@ -94,4 +112,26 @@ library Bytes { } return input; } + + /** + * Splits a `bytes` memory array into a `bytes` memory array of the given lengths. + */ + function split(bytes memory data, uint256[] memory lengths) internal pure returns (bytes[] memory) { + bytes[] memory chunks = new bytes[](lengths.length); + uint256 sum = 0; + for (uint256 i = 0; i < lengths.length; ) { + chunks[i] = new bytes(lengths[i]); + for (uint256 j = 0; j < lengths[i]; ) { + unchecked { + chunks[i][j] = data[sum + j]; + j += 1; + } + } + unchecked { + sum += lengths[i]; + i += 1; + } + } + return chunks; + } } diff --git a/packages/solecs/v2/StoreCore.sol b/packages/solecs/v2/StoreCore.sol index 7f7ebf7a92..9144d6ac3f 100644 --- a/packages/solecs/v2/StoreCore.sol +++ b/packages/solecs/v2/StoreCore.sol @@ -7,25 +7,17 @@ import { console } from "forge-std/console.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 StoreUpdate(bytes32 table, bytes32[] key, uint8 schemaIndex, bytes[] data); + event StoreUpdate(bytes32 table, bytes32[] key, uint8 schemaIndex, bytes data); bytes32 constant _slot = keccak256("mud.store"); bytes32 constant _schemaTable = keccak256("mud.store.table.schema"); error StoreCore_SchemaTooLong(); - /** - * Compute the storage location based on table id and index tuple - * TODO: provide different overloads for single key and some fixed length array keys for better devex - */ - function _getLocation(bytes32 table, bytes32[] memory key) internal pure returns (bytes32) { - return keccak256(abi.encode(_slot, table, key)); - } - - function _getLocation(bytes32 table, bytes32 key) internal pure returns (bytes32) { - bytes32[] memory keyTuple = new bytes32[](1); - keyTuple[0] = key; - return _getLocation(table, keyTuple); - } + /************************************************************************ + * + * SCHEMA + * + ************************************************************************/ /** * Check if the given table exists @@ -62,21 +54,26 @@ library StoreCore { schema = Bytes.toSchemaTypeArray(blob); } + /************************************************************************ + * + * SET DATA + * + ************************************************************************/ + /** * Set data for the given table and key tuple */ function setData( bytes32 table, bytes32[] memory key, - bytes[] memory data + bytes memory data ) internal { // TODO: verify the value has the correct length for the table (based on the table's schema) // (Tradeoff, slightly higher cost due to additional sload, but higher security - library could also provide both options) // Store the provided value in storage bytes32 location = _getLocation(table, key); - bytes memory blob = Bytes.from(data); - _setDataRaw(location, blob); + _setDataRaw(location, data); // Emit event to notify indexers emit StoreUpdate(table, key, 0, data); @@ -88,13 +85,71 @@ library StoreCore { function setData( bytes32 table, bytes32 key, - bytes[] memory data + bytes memory data ) internal { bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = key; setData(table, keyTuple, data); } + /************************************************************************ + * + * GET DATA + * + ************************************************************************/ + + /** + * Get full data for the given table and key tuple (compute length from schema) + */ + function getData(bytes32 table, bytes32[] memory key) internal view returns (bytes memory) { + // Get schema for this table + SchemaType[] memory schema = getSchema(table); + + // Compute length of the full schema + uint256 length = _getByteLength(schema); + + return getData(table, key, length); + } + + /** + * Get full data for the given table and key tuple, with the given length + */ + function getData( + bytes32 table, + bytes32[] memory key, + uint256 length + ) internal view returns (bytes memory) { + // Load the data from storage + bytes32 location = _getLocation(table, key); + return _getDataRaw(location, length); + } + + function getData(bytes32 table, bytes32 key) internal view returns (bytes memory) { + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = key; + return getData(table, keyTuple); + } + + /************************************************************************ + * + * INTERNAL HELPER FUNCTIONS + * + ************************************************************************/ + + /** + * Compute the storage location based on table id and index tuple + * TODO: provide different overloads for single key and some fixed length array keys for better devex + */ + function _getLocation(bytes32 table, bytes32[] memory key) internal pure returns (bytes32) { + return keccak256(abi.encode(_slot, table, key)); + } + + function _getLocation(bytes32 table, bytes32 key) internal pure returns (bytes32) { + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = key; + return _getLocation(table, keyTuple); + } + /** * Write raw bytes to storage at the given location */ @@ -112,23 +167,6 @@ library StoreCore { } } - /** - * Get full data for the given table and key tuple - */ - function getData(bytes32 table, bytes32[] memory key) internal view returns (bytes[] memory data) { - // Get schema for this table - SchemaType[] memory schema = getSchema(table); - - // Compute length of the full schema - uint256 length = _getByteLength(schema); - - // Load the data from storage - bytes32 location = _getLocation(table, key); - bytes memory blob = _getDataRaw(location, length); - - // Split up data into bytes[] based on schema - } - /** * Read raw bytes from storage at the given location and length in bytess */ @@ -149,6 +187,7 @@ library StoreCore { /** * Get the length of the data for the given schema type * (Because Solidity doesn't support constant arrays, we need to use a function) + * TODO: add more types */ function _getByteLength(SchemaType schemaType) internal pure returns (uint256) { if (schemaType == SchemaType.Uint8) { @@ -165,27 +204,26 @@ library StoreCore { */ function _getByteLength(SchemaType[] memory schema) internal pure returns (uint256) { uint256 length = 0; - for (uint256 i = 0; i < schema.length; i++) { + for (uint256 i = 0; i < schema.length; ) { length += _getByteLength(schema[i]); + unchecked { + i++; + } } return length; } /** - * Split the given blob into bytes[] based on the given schema - * TODO: change implementation work on bits instead of bytes - * TODO: gas golf + * Split the given bytes blob into an array of bytes based on the given schema */ - function _split(bytes memory blob, SchemaType[] memory schema) internal pure returns (bytes[] memory output) { - output = new bytes[](schema.length); - uint256 offset; - for (uint256 i = 0; i < schema.length; i++) { - uint256 length = _getByteLength(schema[i]); - output[i] = new bytes(length); - for (uint256 j = 0; j < length; j++) { - output[i][j] = blob[offset + j]; + function _split(bytes memory blob, SchemaType[] memory schema) internal pure returns (bytes[] memory) { + uint256[] memory lengths = new uint256[](schema.length); + for (uint256 i = 0; i < schema.length; ) { + lengths[i] = _getByteLength(schema[i]); + unchecked { + i++; } - offset += length; } + return Bytes.split(blob, lengths); } } diff --git a/packages/solecs/v2/test/StoreCore.t.sol b/packages/solecs/v2/test/StoreCore.t.sol index 329f218f3f..b1eedfcae8 100644 --- a/packages/solecs/v2/test/StoreCore.t.sol +++ b/packages/solecs/v2/test/StoreCore.t.sol @@ -77,10 +77,59 @@ contract StoreCoreTest is DSTestPlus { schema[1] = SchemaType.Uint16; bytes memory data = bytes.concat(bytes1(0x01), bytes2(0x0203)); + + uint256 gas = gasleft(); bytes[] memory splitData = StoreCore._split(data, schema); + gas = gas - gasleft(); + console.log("gas used: %s", gas); assertEq(splitData.length, schema.length); assertEq(uint8(bytes1(splitData[0])), 0x01); assertEq(uint16(bytes2(splitData[1])), 0x0203); } + + function testSetAndGetAndSplitData() public { + // Register table's schema + SchemaType[] memory schema = new SchemaType[](4); + schema[0] = SchemaType.Uint8; + schema[1] = SchemaType.Uint16; + schema[2] = SchemaType.Uint8; + schema[3] = SchemaType.Uint16; + bytes32 table = keccak256("some.table"); + StoreCore.registerSchema(table, schema); + + // Set data + bytes memory data = bytes.concat(bytes1(0x01), bytes2(0x0203), bytes1(0x04), bytes2(0x0506)); + + bytes32[] memory key = new bytes32[](1); + key[0] = keccak256("some.key"); + + uint256 gas = gasleft(); + uint256 length = StoreCore._getByteLength(schema); + gas = gas - gasleft(); + console.log("gas used (compute schema length): %s", gas); + + gas = gasleft(); + StoreCore.setData(table, key, data); + gas = gas - gasleft(); + console.log("gas used (set): %s", gas); + + // Get data + gas = gasleft(); + bytes memory loadedData = StoreCore.getData(table, key, length); + gas = gas - gasleft(); + console.log("gas used (get, warm): %s", gas); + + // Split data + gas = gasleft(); + bytes[] memory splitData = StoreCore._split(data, schema); + gas = gas - gasleft(); + console.log("gas used (split): %s", gas); + + assertEq(loadedData.length, data.length); + assertEq(uint8(bytes1(splitData[0])), 0x01); + assertEq(uint16(bytes2(splitData[1])), 0x0203); + assertEq(uint8(bytes1(splitData[2])), 0x04); + assertEq(uint16(bytes2(splitData[3])), 0x0506); + } } From 6beb9672aef83a62f763a069ec25990f7a4fa3f6 Mon Sep 17 00:00:00 2001 From: alvrs Date: Wed, 18 Jan 2023 13:37:25 +0100 Subject: [PATCH 03/82] feat(solecs): wip data model - add StoreSwitch lib --- packages/solecs/v2/IStore.sol | 18 +++++ packages/solecs/v2/StoreCore.sol | 81 +++++++++++++++-------- packages/solecs/v2/StoreSwitch.sol | 59 +++++++++++++++++ packages/solecs/v2/StoreView.sol | 18 +++++ packages/solecs/v2/tables/Vec2Table.sol | 4 ++ packages/solecs/v2/test/StoreSwitch.t.sol | 68 +++++++++++++++++++ 6 files changed, 221 insertions(+), 27 deletions(-) create mode 100644 packages/solecs/v2/StoreSwitch.sol create mode 100644 packages/solecs/v2/StoreView.sol create mode 100644 packages/solecs/v2/tables/Vec2Table.sol create mode 100644 packages/solecs/v2/test/StoreSwitch.t.sol diff --git a/packages/solecs/v2/IStore.sol b/packages/solecs/v2/IStore.sol index 2ce43a8a4b..f8e53ae2e5 100644 --- a/packages/solecs/v2/IStore.sol +++ b/packages/solecs/v2/IStore.sol @@ -1,7 +1,25 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; +import { SchemaType } from "./Types.sol"; + interface IStore { // note: the preimage of the tuple of keys used to index is part of the event, so it can be used by indexers event StoreUpdate(bytes32 table, bytes32[] key, uint8 schemaIndex, bytes[] data); + + function registerSchema(bytes32 table, SchemaType[] memory schema) external; + + function getSchema(bytes32 table) external view returns (SchemaType[] memory schema); + + function setData( + bytes32 table, + bytes32[] memory key, + bytes memory data + ) external; + + function getData(bytes32 table, bytes32[] memory key) external view returns (bytes memory); + + // If this function exists on the contract, it is a store + // TODO: benchmark this vs. using a known storage slot to determine whether a contract is a Store + function isStore() external view; } diff --git a/packages/solecs/v2/StoreCore.sol b/packages/solecs/v2/StoreCore.sol index 9144d6ac3f..1b50ccd90c 100644 --- a/packages/solecs/v2/StoreCore.sol +++ b/packages/solecs/v2/StoreCore.sol @@ -32,7 +32,9 @@ library StoreCore { function registerSchema(bytes32 table, SchemaType[] memory schema) internal { // TODO: verify the table doesn't already exist if (schema.length > 32) revert StoreCore_SchemaTooLong(); - bytes32 location = _getLocation(_schemaTable, table); + bytes32[] memory key = new bytes32[](1); + key[0] = table; + bytes32 location = _getLocation(_schemaTable, key); _setDataRaw(location, Bytes.from(schema)); } @@ -40,7 +42,9 @@ library StoreCore { * Get the schema for the given table */ function getSchema(bytes32 table) internal view returns (SchemaType[] memory schema) { - bytes32 location = _getLocation(_schemaTable, table); + bytes32[] memory key = new bytes32[](1); + key[0] = table; + bytes32 location = _getLocation(_schemaTable, key); bytes memory blob = _getDataRaw(location, 32); // Find the first `None` value in the schema to determine the length @@ -79,19 +83,6 @@ library StoreCore { emit StoreUpdate(table, key, 0, data); } - /** - * Set data for the given table and single key - */ - function setData( - bytes32 table, - bytes32 key, - bytes memory data - ) internal { - bytes32[] memory keyTuple = new bytes32[](1); - keyTuple[0] = key; - setData(table, keyTuple, data); - } - /************************************************************************ * * GET DATA @@ -124,12 +115,6 @@ library StoreCore { return _getDataRaw(location, length); } - function getData(bytes32 table, bytes32 key) internal view returns (bytes memory) { - bytes32[] memory keyTuple = new bytes32[](1); - keyTuple[0] = key; - return getData(table, keyTuple); - } - /************************************************************************ * * INTERNAL HELPER FUNCTIONS @@ -144,12 +129,6 @@ library StoreCore { return keccak256(abi.encode(_slot, table, key)); } - function _getLocation(bytes32 table, bytes32 key) internal pure returns (bytes32) { - bytes32[] memory keyTuple = new bytes32[](1); - keyTuple[0] = key; - return _getLocation(table, keyTuple); - } - /** * Write raw bytes to storage at the given location */ @@ -227,3 +206,51 @@ library StoreCore { return Bytes.split(blob, lengths); } } + +// Overloads for single key and some fixed length array keys for better devex +library StoreCoreExt { + /************************************************************************ + * + * SET DATA + * + ************************************************************************/ + + function setData( + bytes32 table, + bytes32 _key, + bytes memory data + ) internal { + bytes32[] memory key = new bytes32[](1); + key[0] = _key; + StoreCore.setData(table, key, data); + } + + function setData( + bytes32 table, + bytes32[2] memory _key, + bytes memory data + ) internal { + bytes32[] memory key = new bytes32[](2); + key[0] = _key[0]; + key[1] = _key[1]; + StoreCore.setData(table, key, data); + } + + /************************************************************************ + * + * GET DATA + * + ************************************************************************/ + function getData(bytes32 table, bytes32 _key) external view returns (bytes memory) { + bytes32[] memory key = new bytes32[](1); + key[0] = _key; + return StoreCore.getData(table, key); + } + + function getData(bytes32 table, bytes32[2] memory _key) external view returns (bytes memory) { + bytes32[] memory key = new bytes32[](2); + key[0] = _key[0]; + key[1] = _key[1]; + return StoreCore.getData(table, key); + } +} diff --git a/packages/solecs/v2/StoreSwitch.sol b/packages/solecs/v2/StoreSwitch.sol new file mode 100644 index 0000000000..e63a5575a1 --- /dev/null +++ b/packages/solecs/v2/StoreSwitch.sol @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import { SchemaType } from "./Types.sol"; +import { IStore } from "./IStore.sol"; +import { StoreCore } from "./StoreCore.sol"; + +/** + * Call IStore functions on self or msg.sender, depending on whether the call is a delegatecall or regular call. + */ +library StoreSwitch { + /** + * Detect whether the current call is a delegatecall or regular call. + * (The isStore method doesn't return a value to save gas, but it if exists, the call will succeed.) + */ + function isDelegateCall() internal view returns (bool success) { + try IStore(address(this)).isStore() { + success = true; + } catch { + success = false; + } + } + + function registerSchema(bytes32 table, SchemaType[] memory schema) internal { + if (isDelegateCall()) { + StoreCore.registerSchema(table, schema); + } else { + IStore(msg.sender).registerSchema(table, schema); + } + } + + function getSchema(bytes32 table) internal view returns (SchemaType[] memory schema) { + if (isDelegateCall()) { + schema = StoreCore.getSchema(table); + } else { + schema = IStore(msg.sender).getSchema(table); + } + } + + function setData( + bytes32 table, + bytes32[] memory key, + bytes memory data + ) internal { + if (isDelegateCall()) { + StoreCore.setData(table, key, data); + } else { + IStore(msg.sender).setData(table, key, data); + } + } + + function getData(bytes32 table, bytes32[] memory key) internal view returns (bytes memory) { + if (isDelegateCall()) { + return StoreCore.getData(table, key); + } else { + return IStore(msg.sender).getData(table, key); + } + } +} diff --git a/packages/solecs/v2/StoreView.sol b/packages/solecs/v2/StoreView.sol new file mode 100644 index 0000000000..f0ef3158c0 --- /dev/null +++ b/packages/solecs/v2/StoreView.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import { SchemaType } from "./Types.sol"; +import { IStore } from "./IStore.sol"; +import { StoreCore } from "./StoreCore.sol"; + +abstract contract StoreView is IStore { + function getSchema(bytes32 table) public view virtual returns (SchemaType[] memory schema) { + schema = StoreCore.getSchema(table); + } + + function getData(bytes32 table, bytes32[] memory key) public view virtual returns (bytes memory) { + return StoreCore.getData(table, key); + } + + function isStore() public view {} +} diff --git a/packages/solecs/v2/tables/Vec2Table.sol b/packages/solecs/v2/tables/Vec2Table.sol new file mode 100644 index 0000000000..aa1368bf69 --- /dev/null +++ b/packages/solecs/v2/tables/Vec2Table.sol @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +library Vec2Table {} diff --git a/packages/solecs/v2/test/StoreSwitch.t.sol b/packages/solecs/v2/test/StoreSwitch.t.sol new file mode 100644 index 0000000000..918923f28e --- /dev/null +++ b/packages/solecs/v2/test/StoreSwitch.t.sol @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import { console } from "forge-std/console.sol"; +import { DSTestPlus } from "solmate/test/utils/DSTestPlus.sol"; +import { SchemaType } from "../Types.sol"; +import { StoreCore } from "../StoreCore.sol"; +import { StoreView } from "../StoreView.sol"; +import { StoreSwitch } from "../StoreSwitch.sol"; + +// Mock contract implementing IStore without access control +contract Store is StoreView { + System mockSystem = new System(); + + function registerSchema(bytes32 table, SchemaType[] memory schema) public { + StoreCore.registerSchema(table, schema); + } + + function setData( + bytes32 table, + bytes32[] memory key, + bytes memory data + ) public { + StoreCore.setData(table, key, data); + } + + function callViaDelegateCall() public returns (bool isDelegate) { + (bool success, bytes memory data) = address(mockSystem).delegatecall(abi.encodeWithSignature("isDelegateCall()")); + if (!success) revert("delegatecall failed"); + isDelegate = abi.decode(data, (bool)); + } + + function callViaCall() public returns (bool isDelegate) { + (bool success, bytes memory data) = address(mockSystem).call(abi.encodeWithSignature("isDelegateCall()")); + if (!success) revert("delegatecall failed"); + isDelegate = abi.decode(data, (bool)); + } +} + +// Mock system to wrap StoreSwitch.isDelegateCall() +contract System { + function isDelegateCall() public view returns (bool isDelegate) { + uint256 gas = gasleft(); + isDelegate = StoreSwitch.isDelegateCall(); + gas = gas - gasleft(); + console.log("gas used: %s", gas); + } +} + +contract StoreSwitchTest is DSTestPlus { + Store store; + + function setUp() public { + store = new Store(); + } + + function testIsDelegatecall() public { + bool isDelegate = store.callViaDelegateCall(); + assertTrue(isDelegate); + } + + function testIsNoDelegatecall() public { + bool isDelegate = store.callViaCall(); + assertFalse(isDelegate); + } + + // TODO: tests for setting data on self vs msg.sender +} From f6000c4edf7b662806909c17c5c2713017738b9c Mon Sep 17 00:00:00 2001 From: alvrs Date: Wed, 18 Jan 2023 15:21:05 +0100 Subject: [PATCH 04/82] feat(solecs): wip data model - add Vec2Table and tests --- packages/solecs/v2/Bytes.sol | 13 +++- packages/solecs/v2/IStore.sol | 23 +++++++ packages/solecs/v2/StoreCore.sol | 80 ++++++++++++++--------- packages/solecs/v2/StoreSwitch.sol | 37 +++++++++++ packages/solecs/v2/StoreView.sol | 42 +++++++++++- packages/solecs/v2/Types.sol | 20 +++++- packages/solecs/v2/tables/Vec2Table.sol | 69 ++++++++++++++++++- packages/solecs/v2/test/Bytes.t.sol | 18 +++++ packages/solecs/v2/test/StoreCore.t.sol | 4 +- packages/solecs/v2/test/StoreSwitch.t.sol | 14 +--- packages/solecs/v2/test/Vec2Table.t.sol | 41 ++++++++++++ 11 files changed, 309 insertions(+), 52 deletions(-) create mode 100644 packages/solecs/v2/test/Vec2Table.t.sol diff --git a/packages/solecs/v2/Bytes.sol b/packages/solecs/v2/Bytes.sol index ea9cde1e56..98c92d34b1 100644 --- a/packages/solecs/v2/Bytes.sol +++ b/packages/solecs/v2/Bytes.sol @@ -38,6 +38,10 @@ library Bytes { } } + function from(uint32 input) internal pure returns (bytes memory output) { + return bytes.concat(bytes4(input)); + } + /************************************************************************ * * BYTES -> ANYTHING @@ -61,14 +65,18 @@ library Bytes { * Converts a `bytes` memory blob to a single `bytes32` memory value. */ function toBytes32(bytes memory input) internal pure returns (bytes32 output) { - return toBytes32(input, 0); + return bytes32(input); } /** * Converts a `bytes` memory blob to a single `uint256` memory value. */ function toUint256(bytes memory input) internal pure returns (uint256 output) { - return uint256(toBytes32(input)); + return uint256(bytes32(input)); + } + + function toUint32(bytes memory input) internal pure returns (uint32 output) { + return uint32(bytes4(input)); } /** @@ -115,6 +123,7 @@ library Bytes { /** * Splits a `bytes` memory array into a `bytes` memory array of the given lengths. + * TODO: optimize gas cost */ function split(bytes memory data, uint256[] memory lengths) internal pure returns (bytes[] memory) { bytes[] memory chunks = new bytes[](lengths.length); diff --git a/packages/solecs/v2/IStore.sol b/packages/solecs/v2/IStore.sol index f8e53ae2e5..38b3ce2975 100644 --- a/packages/solecs/v2/IStore.sol +++ b/packages/solecs/v2/IStore.sol @@ -11,14 +11,37 @@ interface IStore { function getSchema(bytes32 table) external view returns (SchemaType[] memory schema); + // Set full data function setData( bytes32 table, bytes32[] memory key, bytes memory data ) external; + // Set partial data + function setData( + bytes32 table, + bytes32[] memory key, + uint8 schemaIndex, + bytes memory data + ) external; + + // Get full data function getData(bytes32 table, bytes32[] memory key) external view returns (bytes memory); + function getData( + bytes32 table, + bytes32[] memory key, + uint256 length + ) external view returns (bytes memory); + + // Get partial data + function getPartialData( + bytes32 table, + bytes32[] memory key, + uint8 schemaIndex + ) external view returns (bytes memory); + // If this function exists on the contract, it is a store // TODO: benchmark this vs. using a known storage slot to determine whether a contract is a Store function isStore() external view; diff --git a/packages/solecs/v2/StoreCore.sol b/packages/solecs/v2/StoreCore.sol index 1b50ccd90c..9517929c9c 100644 --- a/packages/solecs/v2/StoreCore.sol +++ b/packages/solecs/v2/StoreCore.sol @@ -2,7 +2,7 @@ pragma solidity >=0.8.0; import { Utils } from "./Utils.sol"; import { Bytes } from "./Bytes.sol"; -import { SchemaType } from "./Types.sol"; +import { SchemaType, getByteLength } from "./Types.sol"; import { console } from "forge-std/console.sol"; library StoreCore { @@ -65,7 +65,7 @@ library StoreCore { ************************************************************************/ /** - * Set data for the given table and key tuple + * Set full data for the given table and key tuple */ function setData( bytes32 table, @@ -83,6 +83,19 @@ library StoreCore { emit StoreUpdate(table, key, 0, data); } + /** + * Set partial data for the given table and key tuple, at the given schema index + * TODO: implement + */ + function setData( + bytes32 table, + bytes32[] memory key, + uint8 schemaIndex, + bytes memory data + ) internal { + revert("not implemented"); + } + /************************************************************************ * * GET DATA @@ -102,6 +115,18 @@ library StoreCore { return getData(table, key, length); } + /** + * Get partial data for the given table and key tuple, at the given schema index + * TODO: implement + */ + function getPartialData( + bytes32 table, + bytes32[] memory key, + uint8 schemaIndex + ) internal view returns (bytes memory) { + revert("not implemented"); + } + /** * Get full data for the given table and key tuple, with the given length */ @@ -115,6 +140,26 @@ library StoreCore { return _getDataRaw(location, length); } + /************************************************************************ + * + * HELPER FUNCTIONS + * + ************************************************************************/ + + /** + * Split the given bytes blob into an array of bytes based on the given schema + */ + function split(bytes memory blob, SchemaType[] memory schema) internal pure returns (bytes[] memory) { + uint256[] memory lengths = new uint256[](schema.length); + for (uint256 i = 0; i < schema.length; ) { + lengths[i] = getByteLength(schema[i]); + unchecked { + i++; + } + } + return Bytes.split(blob, lengths); + } + /************************************************************************ * * INTERNAL HELPER FUNCTIONS @@ -163,48 +208,19 @@ library StoreCore { } } - /** - * Get the length of the data for the given schema type - * (Because Solidity doesn't support constant arrays, we need to use a function) - * TODO: add more types - */ - function _getByteLength(SchemaType schemaType) internal pure returns (uint256) { - if (schemaType == SchemaType.Uint8) { - return 1; - } else if (schemaType == SchemaType.Uint16) { - return 2; - } else { - revert("Unsupported schema type"); - } - } - /** * Get the length of the data for the given schema */ function _getByteLength(SchemaType[] memory schema) internal pure returns (uint256) { uint256 length = 0; for (uint256 i = 0; i < schema.length; ) { - length += _getByteLength(schema[i]); + length += getByteLength(schema[i]); unchecked { i++; } } return length; } - - /** - * Split the given bytes blob into an array of bytes based on the given schema - */ - function _split(bytes memory blob, SchemaType[] memory schema) internal pure returns (bytes[] memory) { - uint256[] memory lengths = new uint256[](schema.length); - for (uint256 i = 0; i < schema.length; ) { - lengths[i] = _getByteLength(schema[i]); - unchecked { - i++; - } - } - return Bytes.split(blob, lengths); - } } // Overloads for single key and some fixed length array keys for better devex diff --git a/packages/solecs/v2/StoreSwitch.sol b/packages/solecs/v2/StoreSwitch.sol index e63a5575a1..814803a23e 100644 --- a/packages/solecs/v2/StoreSwitch.sol +++ b/packages/solecs/v2/StoreSwitch.sol @@ -49,6 +49,19 @@ library StoreSwitch { } } + function setData( + bytes32 table, + bytes32[] memory key, + uint8 schemaIndex, + bytes memory data + ) internal { + if (isDelegateCall()) { + StoreCore.setData(table, key, schemaIndex, data); + } else { + IStore(msg.sender).setData(table, key, schemaIndex, data); + } + } + function getData(bytes32 table, bytes32[] memory key) internal view returns (bytes memory) { if (isDelegateCall()) { return StoreCore.getData(table, key); @@ -56,4 +69,28 @@ library StoreSwitch { return IStore(msg.sender).getData(table, key); } } + + function getData( + bytes32 table, + bytes32[] memory key, + uint256 length + ) internal view returns (bytes memory) { + if (isDelegateCall()) { + return StoreCore.getData(table, key, length); + } else { + return IStore(msg.sender).getData(table, key, length); + } + } + + function getPartialData( + bytes32 table, + bytes32[] memory key, + uint8 schemaIndex + ) internal view returns (bytes memory) { + if (isDelegateCall()) { + return StoreCore.getPartialData(table, key, schemaIndex); + } else { + return IStore(msg.sender).getPartialData(table, key, schemaIndex); + } + } } diff --git a/packages/solecs/v2/StoreView.sol b/packages/solecs/v2/StoreView.sol index f0ef3158c0..2bffa75f4b 100644 --- a/packages/solecs/v2/StoreView.sol +++ b/packages/solecs/v2/StoreView.sol @@ -5,7 +5,31 @@ import { SchemaType } from "./Types.sol"; import { IStore } from "./IStore.sol"; import { StoreCore } from "./StoreCore.sol"; -abstract contract StoreView is IStore { +// Not abstract, so that it can be used as a base contract for testing and if write access is not needed +contract StoreView is IStore { + error Store_BaseContractNotImplemented(); + + function registerSchema(bytes32, SchemaType[] memory) public virtual { + revert Store_BaseContractNotImplemented(); + } + + function setData( + bytes32, + bytes32[] memory, + bytes memory + ) public virtual { + revert Store_BaseContractNotImplemented(); + } + + function setData( + bytes32, + bytes32[] memory, + uint8, + bytes memory + ) public virtual { + revert Store_BaseContractNotImplemented(); + } + function getSchema(bytes32 table) public view virtual returns (SchemaType[] memory schema) { schema = StoreCore.getSchema(table); } @@ -14,5 +38,21 @@ abstract contract StoreView is IStore { return StoreCore.getData(table, key); } + function getData( + bytes32 table, + bytes32[] memory key, + uint256 length + ) public view virtual returns (bytes memory) { + return StoreCore.getData(table, key, length); + } + + function getPartialData( + bytes32 table, + bytes32[] memory key, + uint8 schemaIndex + ) public view virtual returns (bytes memory) { + return StoreCore.getPartialData(table, key, schemaIndex); + } + function isStore() public view {} } diff --git a/packages/solecs/v2/Types.sol b/packages/solecs/v2/Types.sol index cf38a66f1e..018c93e6de 100644 --- a/packages/solecs/v2/Types.sol +++ b/packages/solecs/v2/Types.sol @@ -5,5 +5,23 @@ pragma solidity >=0.8.0; enum SchemaType { None, // The first `None` value ends the schema Uint8, - Uint16 + Uint16, + Uint32 +} + +/** + * Get the length of the data for the given schema type + * (Because Solidity doesn't support constant arrays, we need to use a function) + * TODO: add more types and make it more efficient (avoid linear search) + */ +function getByteLength(SchemaType schemaType) pure returns (uint256) { + if (schemaType == SchemaType.Uint8) { + return 1; + } else if (schemaType == SchemaType.Uint16) { + return 2; + } else if (schemaType == SchemaType.Uint32) { + return 4; + } else { + revert("Unsupported schema type"); + } } diff --git a/packages/solecs/v2/tables/Vec2Table.sol b/packages/solecs/v2/tables/Vec2Table.sol index aa1368bf69..ae35b2eec7 100644 --- a/packages/solecs/v2/tables/Vec2Table.sol +++ b/packages/solecs/v2/tables/Vec2Table.sol @@ -1,4 +1,71 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; -library Vec2Table {} +import { console } from "forge-std/console.sol"; +import { StoreSwitch } from "../StoreSwitch.sol"; +import { StoreCore } from "../StoreCore.sol"; +import { SchemaType } from "../Types.sol"; +import { Bytes } from "../Bytes.sol"; + +// -- User defined schema and id -- + +bytes32 constant id = keccak256("mud.store.table.vec2"); + +struct Schema { + uint32 x; + uint32 y; +} + +// -- Autogenerated schema and library -- +// TODO: autogenerate + +library Vec2Table { + /** Get the table's schema */ + function getSchema() internal pure returns (SchemaType[] memory schema) { + schema = new SchemaType[](2); + schema[0] = SchemaType.Uint32; + schema[1] = SchemaType.Uint32; + } + + /** Register the table's schema */ + function registerSchema() internal { + StoreSwitch.registerSchema(id, getSchema()); + } + + /** Set the table's data */ + function set( + bytes32 key, + uint32 x, + uint32 y + ) internal { + bytes memory data = bytes.concat(bytes4(x), bytes4(y)); + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = key; + StoreSwitch.setData(id, keyTuple, data); + } + + function set(bytes32 key, Schema memory vec2) internal { + set(key, vec2.x, vec2.y); + } + + function setX(bytes32 key, uint32 x) internal { + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = key; + StoreSwitch.setData(id, keyTuple, 0, bytes.concat(bytes4(x))); + } + + function setY(bytes32 key, uint32 y) internal { + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = key; + StoreSwitch.setData(id, keyTuple, 1, bytes.concat(bytes4(y))); + } + + /** Get the table's data */ + function get(bytes32 key) internal view returns (Schema memory vec2) { + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = key; + bytes memory blob = StoreSwitch.getData(id, keyTuple, 8); + bytes[] memory data = StoreCore.split(blob, getSchema()); + return Schema({ x: Bytes.toUint32(data[0]), y: Bytes.toUint32(data[1]) }); + } +} diff --git a/packages/solecs/v2/test/Bytes.t.sol b/packages/solecs/v2/test/Bytes.t.sol index 293ffd4d69..b806fb23de 100644 --- a/packages/solecs/v2/test/Bytes.t.sol +++ b/packages/solecs/v2/test/Bytes.t.sol @@ -90,6 +90,24 @@ contract BytesTest is DSTestPlus { assertEq(uint256(output[2]), 0x0500000000000000000000000000000000000000000000000000000000000000); } + function testToAndFromUint32() public { + uint32 input = 0x01000002; + + uint256 gas = gasleft(); + bytes memory output = Bytes.from(input); + gas = gas - gasleft(); + console.log("gas used (uint32 -> bytes): %s", gas); + + assertEq(output.length, 4); + + gas = gasleft(); + uint32 output2 = Bytes.toUint32(output); + gas = gas - gasleft(); + console.log("gas used (bytes -> uint32): %s", gas); + + assertEq(output2, input); + } + function testEquals() public { bytes memory a = bytes("a"); bytes memory b = bytes("a"); diff --git a/packages/solecs/v2/test/StoreCore.t.sol b/packages/solecs/v2/test/StoreCore.t.sol index b1eedfcae8..0401a488e2 100644 --- a/packages/solecs/v2/test/StoreCore.t.sol +++ b/packages/solecs/v2/test/StoreCore.t.sol @@ -79,7 +79,7 @@ contract StoreCoreTest is DSTestPlus { bytes memory data = bytes.concat(bytes1(0x01), bytes2(0x0203)); uint256 gas = gasleft(); - bytes[] memory splitData = StoreCore._split(data, schema); + bytes[] memory splitData = StoreCore.split(data, schema); gas = gas - gasleft(); console.log("gas used: %s", gas); @@ -122,7 +122,7 @@ contract StoreCoreTest is DSTestPlus { // Split data gas = gasleft(); - bytes[] memory splitData = StoreCore._split(data, schema); + bytes[] memory splitData = StoreCore.split(data, schema); gas = gas - gasleft(); console.log("gas used (split): %s", gas); diff --git a/packages/solecs/v2/test/StoreSwitch.t.sol b/packages/solecs/v2/test/StoreSwitch.t.sol index 918923f28e..62ea27971c 100644 --- a/packages/solecs/v2/test/StoreSwitch.t.sol +++ b/packages/solecs/v2/test/StoreSwitch.t.sol @@ -8,22 +8,10 @@ import { StoreCore } from "../StoreCore.sol"; import { StoreView } from "../StoreView.sol"; import { StoreSwitch } from "../StoreSwitch.sol"; -// Mock contract implementing IStore without access control +// Mock Store to call MockSystem contract Store is StoreView { System mockSystem = new System(); - function registerSchema(bytes32 table, SchemaType[] memory schema) public { - StoreCore.registerSchema(table, schema); - } - - function setData( - bytes32 table, - bytes32[] memory key, - bytes memory data - ) public { - StoreCore.setData(table, key, data); - } - function callViaDelegateCall() public returns (bool isDelegate) { (bool success, bytes memory data) = address(mockSystem).delegatecall(abi.encodeWithSignature("isDelegateCall()")); if (!success) revert("delegatecall failed"); diff --git a/packages/solecs/v2/test/Vec2Table.t.sol b/packages/solecs/v2/test/Vec2Table.t.sol new file mode 100644 index 0000000000..5eff69ed00 --- /dev/null +++ b/packages/solecs/v2/test/Vec2Table.t.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import { console } from "forge-std/console.sol"; +import { DSTestPlus } from "solmate/test/utils/DSTestPlus.sol"; +import { Vec2Table, id as Vec2Id, Schema as Vec2 } from "../tables/Vec2Table.sol"; +import { StoreCore } from "../StoreCore.sol"; +import { SchemaType } from "../Types.sol"; +import { StoreView } from "../StoreView.sol"; + +contract Vec2TableTest is DSTestPlus, StoreView { + function testRegisterAndGetSchema() public { + uint256 gas = gasleft(); + Vec2Table.registerSchema(); + gas = gas - gasleft(); + console.log("gas used: %s", gas); + + SchemaType[] memory registeredSchema = StoreCore.getSchema(Vec2Id); + SchemaType[] memory declaredSchema = Vec2Table.getSchema(); + + assertEq(keccak256(abi.encode(registeredSchema)), keccak256(abi.encode(declaredSchema))); + } + + function testSetAndGet() public { + Vec2Table.registerSchema(); + bytes32 key = keccak256("somekey"); + + uint256 gas = gasleft(); + Vec2Table.set({ key: key, x: 1, y: 2 }); + gas = gas - gasleft(); + console.log("gas used (set): %s", gas); + + gas = gasleft(); + Vec2 memory vec2 = Vec2Table.get(key); + gas = gas - gasleft(); + console.log("gas used (get, warm): %s", gas); + + assertEq(vec2.x, 1); + assertEq(vec2.y, 2); + } +} From f9587746b3665f367746186d471abb9cda0d74dd Mon Sep 17 00:00:00 2001 From: alvrs Date: Wed, 18 Jan 2023 16:20:09 +0100 Subject: [PATCH 05/82] feat(solecs): wip data model - improve gas for slice/get --- packages/solecs/v2/Bytes.sol | 81 +++++++++++++++++++++---- packages/solecs/v2/tables/Vec2Table.sol | 7 ++- packages/solecs/v2/test/Bytes.t.sol | 19 ++++++ packages/solecs/v2/test/StoreCore.t.sol | 8 ++- 4 files changed, 100 insertions(+), 15 deletions(-) diff --git a/packages/solecs/v2/Bytes.sol b/packages/solecs/v2/Bytes.sol index 98c92d34b1..c567f974f7 100644 --- a/packages/solecs/v2/Bytes.sol +++ b/packages/solecs/v2/Bytes.sol @@ -61,6 +61,72 @@ library Bytes { } } + // From https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol + function slice( + bytes memory _bytes, + uint256 _start, + uint256 _length + ) internal pure returns (bytes memory) { + require(_length + 31 >= _length, "slice_overflow"); + require(_bytes.length >= _start + _length, "slice_outOfBounds"); + + bytes memory tempBytes; + + assembly { + switch iszero(_length) + case 0 { + // Get a location of some free memory and store it in tempBytes as + // Solidity does for memory variables. + tempBytes := mload(0x40) + + // The first word of the slice result is potentially a partial + // word read from the original array. To read it, we calculate + // the length of that partial word and start copying that many + // bytes into the array. The first word we copy will start with + // data we don't care about, but the last `lengthmod` bytes will + // land at the beginning of the contents of the new array. When + // we're done copying, we overwrite the full first word with + // the actual length of the slice. + let lengthmod := and(_length, 31) + + // The multiplication in the next line is necessary + // because when slicing multiples of 32 bytes (lengthmod == 0) + // the following copy loop was copying the origin's length + // and then ending prematurely not copying everything it should. + let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod))) + let end := add(mc, _length) + + for { + // The multiplication in the next line has the same exact purpose + // as the one above. + let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start) + } lt(mc, end) { + mc := add(mc, 0x20) + cc := add(cc, 0x20) + } { + mstore(mc, mload(cc)) + } + + mstore(tempBytes, _length) + + //update free-memory pointer + //allocating the array padded to 32 bytes like the compiler does now + mstore(0x40, and(add(mc, 31), not(31))) + } + //if we want a zero-length slice let's just return a zero-length array + default { + tempBytes := mload(0x40) + //zero out the 32 bytes slice we are about to return + //we need to do it because Solidity does not garbage collect + mstore(tempBytes, 0) + + mstore(0x40, add(tempBytes, 0x20)) + } + } + + return tempBytes; + } + /** * Converts a `bytes` memory blob to a single `bytes32` memory value. */ @@ -123,19 +189,12 @@ library Bytes { /** * Splits a `bytes` memory array into a `bytes` memory array of the given lengths. - * TODO: optimize gas cost */ - function split(bytes memory data, uint256[] memory lengths) internal pure returns (bytes[] memory) { - bytes[] memory chunks = new bytes[](lengths.length); - uint256 sum = 0; + function split(bytes memory data, uint256[] memory lengths) internal pure returns (bytes[] memory chunks) { + chunks = new bytes[](lengths.length); + uint256 sum; for (uint256 i = 0; i < lengths.length; ) { - chunks[i] = new bytes(lengths[i]); - for (uint256 j = 0; j < lengths[i]; ) { - unchecked { - chunks[i][j] = data[sum + j]; - j += 1; - } - } + chunks[i] = slice(data, sum, lengths[i]); unchecked { sum += lengths[i]; i += 1; diff --git a/packages/solecs/v2/tables/Vec2Table.sol b/packages/solecs/v2/tables/Vec2Table.sol index ae35b2eec7..e3a9a7210d 100644 --- a/packages/solecs/v2/tables/Vec2Table.sol +++ b/packages/solecs/v2/tables/Vec2Table.sol @@ -65,7 +65,10 @@ library Vec2Table { bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = key; bytes memory blob = StoreSwitch.getData(id, keyTuple, 8); - bytes[] memory data = StoreCore.split(blob, getSchema()); - return Schema({ x: Bytes.toUint32(data[0]), y: Bytes.toUint32(data[1]) }); + return Schema({ x: Bytes.toUint32(Bytes.slice(blob, 0, 4)), y: Bytes.toUint32(Bytes.slice(blob, 4, 4)) }); + + // Alternative approach, but more expensive: + // bytes[] memory data = StoreCore.split(blob, getSchema()); + // return Schema({ x: Bytes.toUint32(data[0]), y: Bytes.toUint32(data[1]) }); } } diff --git a/packages/solecs/v2/test/Bytes.t.sol b/packages/solecs/v2/test/Bytes.t.sol index b806fb23de..985e1863ea 100644 --- a/packages/solecs/v2/test/Bytes.t.sol +++ b/packages/solecs/v2/test/Bytes.t.sol @@ -146,4 +146,23 @@ contract BytesTest is DSTestPlus { assertEq(a.length, 2); } + + function testSlice() public { + bytes memory a = new bytes(5); + a[0] = 0x01; + a[1] = 0x02; + a[2] = 0x03; + a[3] = 0x04; + a[4] = 0x05; + + uint256 gas = gasleft(); + bytes memory b = Bytes.slice(a, 1, 3); + gas = gas - gasleft(); + console.log("gas used: %s", gas); + + assertEq(b.length, 3); + assertEq(uint256(uint8(b[0])), 0x02); + assertEq(uint256(uint8(b[1])), 0x03); + assertEq(uint256(uint8(b[2])), 0x04); + } } diff --git a/packages/solecs/v2/test/StoreCore.t.sol b/packages/solecs/v2/test/StoreCore.t.sol index 0401a488e2..98fc6c5df6 100644 --- a/packages/solecs/v2/test/StoreCore.t.sol +++ b/packages/solecs/v2/test/StoreCore.t.sol @@ -72,11 +72,13 @@ contract StoreCoreTest is DSTestPlus { } function testSplit() public { - SchemaType[] memory schema = new SchemaType[](2); + SchemaType[] memory schema = new SchemaType[](4); schema[0] = SchemaType.Uint8; schema[1] = SchemaType.Uint16; + schema[2] = SchemaType.Uint16; + schema[3] = SchemaType.Uint32; - bytes memory data = bytes.concat(bytes1(0x01), bytes2(0x0203)); + bytes memory data = bytes.concat(bytes1(0x01), bytes2(0x0203), bytes2(0x0405), bytes4(0x06070809)); uint256 gas = gasleft(); bytes[] memory splitData = StoreCore.split(data, schema); @@ -86,6 +88,8 @@ contract StoreCoreTest is DSTestPlus { assertEq(splitData.length, schema.length); assertEq(uint8(bytes1(splitData[0])), 0x01); assertEq(uint16(bytes2(splitData[1])), 0x0203); + assertEq(uint16(bytes2(splitData[2])), 0x0405); + assertEq(uint32(bytes4(splitData[3])), 0x06070809); } function testSetAndGetAndSplitData() public { From 432ec65eed7215045028bb516ead6d669811765b Mon Sep 17 00:00:00 2001 From: alvrs Date: Wed, 18 Jan 2023 16:52:47 +0100 Subject: [PATCH 06/82] feat(solecs): wip data model - add SystemTable and tests --- packages/solecs/v2/Bytes.sol | 139 ++++++++++++---------- packages/solecs/v2/Types.sol | 13 +- packages/solecs/v2/tables/SystemTable.sol | 84 +++++++++++++ packages/solecs/v2/test/Bytes.t.sol | 56 ++++++++- packages/solecs/v2/test/SystemTable.t.sol | 46 +++++++ 5 files changed, 275 insertions(+), 63 deletions(-) create mode 100644 packages/solecs/v2/tables/SystemTable.sol create mode 100644 packages/solecs/v2/test/SystemTable.t.sol diff --git a/packages/solecs/v2/Bytes.sol b/packages/solecs/v2/Bytes.sol index c567f974f7..4742b55398 100644 --- a/packages/solecs/v2/Bytes.sol +++ b/packages/solecs/v2/Bytes.sol @@ -38,10 +38,23 @@ library Bytes { } } + // Needs unique name to avoid conflict with `from(uint32)` + function fromUint8(uint8 input) internal pure returns (bytes memory output) { + return bytes.concat(bytes1(input)); + } + function from(uint32 input) internal pure returns (bytes memory output) { return bytes.concat(bytes4(input)); } + function from(address input) internal pure returns (bytes memory output) { + return bytes.concat(bytes20(input)); + } + + function from(bytes4 input) internal pure returns (bytes memory output) { + return bytes.concat(input); + } + /************************************************************************ * * BYTES -> ANYTHING @@ -61,6 +74,72 @@ library Bytes { } } + function toBytes32(bytes memory input) internal pure returns (bytes32 output) { + return bytes32(input); + } + + function toUint256(bytes memory input) internal pure returns (uint256 output) { + return uint256(bytes32(input)); + } + + function toUint32(bytes memory input) internal pure returns (uint32 output) { + return uint32(bytes4(input)); + } + + function toAddress(bytes memory input) internal pure returns (address output) { + return address(bytes20(input)); + } + + function toBytes4(bytes memory input) internal pure returns (bytes4 output) { + return bytes4(input); + } + + function toUint8(bytes memory input) internal pure returns (uint8 output) { + return uint8(input[0]); + } + + /** + * Converts a `bytes` memory blob to a `bytes32` memory array. + */ + function toBytes32Array(bytes memory input) internal pure returns (bytes32[] memory output) { + output = new bytes32[](Utils.divCeil(input.length, 32)); + for (uint256 i = 0; i < output.length; i++) { + output[i] = toBytes32(input, i * 32); + } + return output; + } + + /** + * Converts a `bytes` memory blob to a `SchemaType` memory array. + */ + function toSchemaTypeArray(bytes memory input) internal pure returns (SchemaType[] memory output) { + output = new SchemaType[](input.length); + for (uint256 i = 0; i < output.length; i++) { + output[i] = SchemaType(uint8(input[i])); + } + return output; + } + + /************************************************************************ + * + * BYTES UTILS + * + ************************************************************************/ + + function equals(bytes memory a, bytes memory b) internal pure returns (bool) { + if (a.length != b.length) { + return false; + } + return keccak256(a) == keccak256(b); + } + + function setLengthInPlace(bytes memory input, uint256 length) internal pure returns (bytes memory) { + assembly { + mstore(input, length) + } + return input; + } + // From https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol function slice( bytes memory _bytes, @@ -127,66 +206,6 @@ library Bytes { return tempBytes; } - /** - * Converts a `bytes` memory blob to a single `bytes32` memory value. - */ - function toBytes32(bytes memory input) internal pure returns (bytes32 output) { - return bytes32(input); - } - - /** - * Converts a `bytes` memory blob to a single `uint256` memory value. - */ - function toUint256(bytes memory input) internal pure returns (uint256 output) { - return uint256(bytes32(input)); - } - - function toUint32(bytes memory input) internal pure returns (uint32 output) { - return uint32(bytes4(input)); - } - - /** - * Converts a `bytes` memory blob to a `bytes32` memory array. - */ - function toBytes32Array(bytes memory input) internal pure returns (bytes32[] memory output) { - output = new bytes32[](Utils.divCeil(input.length, 32)); - for (uint256 i = 0; i < output.length; i++) { - output[i] = toBytes32(input, i * 32); - } - return output; - } - - /** - * Converts a `bytes` memory blob to a `SchemaType` memory array. - */ - function toSchemaTypeArray(bytes memory input) internal pure returns (SchemaType[] memory output) { - output = new SchemaType[](input.length); - for (uint256 i = 0; i < output.length; i++) { - output[i] = SchemaType(uint8(input[i])); - } - return output; - } - - /************************************************************************ - * - * BYTES UTILS - * - ************************************************************************/ - - function equals(bytes memory a, bytes memory b) internal pure returns (bool) { - if (a.length != b.length) { - return false; - } - return keccak256(a) == keccak256(b); - } - - function setLengthInPlace(bytes memory input, uint256 length) internal pure returns (bytes memory) { - assembly { - mstore(input, length) - } - return input; - } - /** * Splits a `bytes` memory array into a `bytes` memory array of the given lengths. */ diff --git a/packages/solecs/v2/Types.sol b/packages/solecs/v2/Types.sol index 018c93e6de..23263424da 100644 --- a/packages/solecs/v2/Types.sol +++ b/packages/solecs/v2/Types.sol @@ -6,7 +6,9 @@ enum SchemaType { None, // The first `None` value ends the schema Uint8, Uint16, - Uint32 + Uint32, + Bytes4, + Address } /** @@ -19,9 +21,16 @@ function getByteLength(SchemaType schemaType) pure returns (uint256) { return 1; } else if (schemaType == SchemaType.Uint16) { return 2; - } else if (schemaType == SchemaType.Uint32) { + } else if (schemaType == SchemaType.Uint32 || schemaType == SchemaType.Bytes4) { return 4; + } else if (schemaType == SchemaType.Address) { + return 20; } else { revert("Unsupported schema type"); } } + +enum ExecutionMode { + Delegate, + Autonomous +} diff --git a/packages/solecs/v2/tables/SystemTable.sol b/packages/solecs/v2/tables/SystemTable.sol new file mode 100644 index 0000000000..b937a8414c --- /dev/null +++ b/packages/solecs/v2/tables/SystemTable.sol @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import { console } from "forge-std/console.sol"; +import { StoreSwitch } from "../StoreSwitch.sol"; +import { StoreCore } from "../StoreCore.sol"; +import { SchemaType } from "../Types.sol"; +import { Bytes } from "../Bytes.sol"; + +// -- User defined schema and id -- + +bytes32 constant id = keccak256("mud.store.table.system"); + +struct Schema { + address addr; + bytes4 selector; + uint8 executionMode; +} + +// -- Autogenerated schema and library -- +// TODO: autogenerate + +library SystemTable { + /** Get the table's schema */ + function getSchema() internal pure returns (SchemaType[] memory schema) { + schema = new SchemaType[](3); + schema[0] = SchemaType.Address; + schema[1] = SchemaType.Bytes4; + schema[2] = SchemaType.Uint8; + } + + /** Register the table's schema */ + function registerSchema() internal { + StoreSwitch.registerSchema(id, getSchema()); + } + + /** Set the table's data */ + function set( + bytes32 key, + address addr, + bytes4 selector, + uint8 executionMode + ) internal { + bytes memory data = bytes.concat(bytes20(addr), bytes4(selector), bytes1(executionMode)); + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = key; + StoreSwitch.setData(id, keyTuple, data); + } + + function set(bytes32 key, Schema memory data) internal { + set(key, data.addr, data.selector, data.executionMode); + } + + function setAddress(bytes32 key, address addr) internal { + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = key; + StoreSwitch.setData(id, keyTuple, 0, bytes.concat(bytes20(addr))); + } + + function setSelector(bytes32 key, bytes4 selector) internal { + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = key; + StoreSwitch.setData(id, keyTuple, 1, bytes.concat(bytes4(selector))); + } + + function setExecutionMode(bytes32 key, uint8 executionMode) internal { + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = key; + StoreSwitch.setData(id, keyTuple, 2, bytes.concat(bytes1(executionMode))); + } + + /** Get the table's data */ + function get(bytes32 key) internal view returns (Schema memory data) { + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = key; + bytes memory blob = StoreSwitch.getData(id, keyTuple, 25); + return + Schema({ + addr: Bytes.toAddress(Bytes.slice(blob, 0, 20)), + selector: Bytes.toBytes4(Bytes.slice(blob, 20, 4)), + executionMode: Bytes.toUint8(Bytes.slice(blob, 24, 1)) + }); + } +} diff --git a/packages/solecs/v2/test/Bytes.t.sol b/packages/solecs/v2/test/Bytes.t.sol index 985e1863ea..a8638c2019 100644 --- a/packages/solecs/v2/test/Bytes.t.sol +++ b/packages/solecs/v2/test/Bytes.t.sol @@ -90,7 +90,7 @@ contract BytesTest is DSTestPlus { assertEq(uint256(output[2]), 0x0500000000000000000000000000000000000000000000000000000000000000); } - function testToAndFromUint32() public { + function testFromAndToUint32() public { uint32 input = 0x01000002; uint256 gas = gasleft(); @@ -108,6 +108,60 @@ contract BytesTest is DSTestPlus { assertEq(output2, input); } + function testFromAndToAddress() public { + address input = address(0x0100000000000000000000000000000000000002); + + uint256 gas = gasleft(); + bytes memory output = Bytes.from(input); + gas = gas - gasleft(); + console.log("gas used (address -> bytes): %s", gas); + + assertEq(output.length, 20); + + gas = gasleft(); + address output2 = Bytes.toAddress(output); + gas = gas - gasleft(); + console.log("gas used (bytes -> address): %s", gas); + + assertEq(output2, input); + } + + function testFromAndToUint8() public { + uint8 input = 0x02; + + uint256 gas = gasleft(); + bytes memory output = Bytes.fromUint8(input); + gas = gas - gasleft(); + console.log("gas used (uint8 -> bytes): %s", gas); + + assertEq(output.length, 1); + + gas = gasleft(); + uint8 output2 = Bytes.toUint8(output); + gas = gas - gasleft(); + console.log("gas used (bytes -> uint8): %s", gas); + + assertEq(output2, input); + } + + function testFromAndToBytes4() public { + bytes4 input = bytes4(0x01000002); + + uint256 gas = gasleft(); + bytes memory output = Bytes.from(input); + gas = gas - gasleft(); + console.log("gas used (bytes4 -> bytes): %s", gas); + + assertEq(output.length, 4); + + gas = gasleft(); + bytes4 output2 = Bytes.toBytes4(output); + gas = gas - gasleft(); + console.log("gas used (bytes -> bytes4): %s", gas); + + assertEq(output2, input); + } + function testEquals() public { bytes memory a = bytes("a"); bytes memory b = bytes("a"); diff --git a/packages/solecs/v2/test/SystemTable.t.sol b/packages/solecs/v2/test/SystemTable.t.sol new file mode 100644 index 0000000000..fa2b9744f4 --- /dev/null +++ b/packages/solecs/v2/test/SystemTable.t.sol @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import { console } from "forge-std/console.sol"; +import { DSTestPlus } from "solmate/test/utils/DSTestPlus.sol"; +import { SystemTable, id as SystemTableId, Schema as SystemEntry } from "../tables/SystemTable.sol"; +import { StoreCore } from "../StoreCore.sol"; +import { SchemaType } from "../Types.sol"; +import { StoreView } from "../StoreView.sol"; + +contract SystemTableTest is DSTestPlus, StoreView { + function testRegisterAndGetSchema() public { + uint256 gas = gasleft(); + SystemTable.registerSchema(); + gas = gas - gasleft(); + console.log("gas used: %s", gas); + + SchemaType[] memory registeredSchema = StoreCore.getSchema(SystemTableId); + SchemaType[] memory declaredSchema = SystemTable.getSchema(); + + assertEq(keccak256(abi.encode(registeredSchema)), keccak256(abi.encode(declaredSchema))); + } + + function testSetAndGet() public { + SystemTable.registerSchema(); + bytes32 key = keccak256("somekey"); + + address addr = address(0x1234); + bytes4 selector = bytes4(0x12345678); + uint8 executionMode = 1; + + uint256 gas = gasleft(); + SystemTable.set(key, addr, selector, executionMode); + gas = gas - gasleft(); + console.log("gas used (set): %s", gas); + + gas = gasleft(); + SystemEntry memory systemEntry = SystemTable.get(key); + gas = gas - gasleft(); + console.log("gas used (get, warm): %s", gas); + + assertEq(systemEntry.addr, addr); + assertEq(systemEntry.selector, selector); + assertEq(systemEntry.executionMode, executionMode); + } +} From ebc161db69dc8a9382d688d52d95c0c4d5870243 Mon Sep 17 00:00:00 2001 From: alvrs Date: Thu, 19 Jan 2023 17:20:16 +0100 Subject: [PATCH 07/82] feat(solecs): wip data model - add World and end-to-end tests --- packages/solecs/v2/System.sol | 13 +++ packages/solecs/v2/World.sol | 117 ++++++++++++++++++++++ packages/solecs/v2/tables/SystemTable.sol | 16 +++ packages/solecs/v2/tables/Vec2Table.sol | 16 +++ packages/solecs/v2/test/System.t.sol | 23 +++++ packages/solecs/v2/test/World.t.sol | 115 +++++++++++++++++++++ 6 files changed, 300 insertions(+) create mode 100644 packages/solecs/v2/System.sol create mode 100644 packages/solecs/v2/World.sol create mode 100644 packages/solecs/v2/test/System.t.sol create mode 100644 packages/solecs/v2/test/World.t.sol diff --git a/packages/solecs/v2/System.sol b/packages/solecs/v2/System.sol new file mode 100644 index 0000000000..3d5dfed74e --- /dev/null +++ b/packages/solecs/v2/System.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +// TODO: Use existing ERC2771 implementations +contract System { + // Extract the trusted msg.sender value appended to the calldata + function _msgSender() internal pure returns (address sender) { + assembly { + // 96 = 256 - 20 * 8 + sender := shr(96, calldataload(sub(calldatasize(), 20))) + } + } +} diff --git a/packages/solecs/v2/World.sol b/packages/solecs/v2/World.sol new file mode 100644 index 0000000000..694b41bf24 --- /dev/null +++ b/packages/solecs/v2/World.sol @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import { console } from "forge-std/console.sol"; +import { StoreCore } from "./StoreCore.sol"; +import { StoreView } from "./StoreView.sol"; +import { SchemaType, ExecutionMode } from "./Types.sol"; +import { SystemTable, Schema as SystemTableEntry } from "./tables/SystemTable.sol"; +import { Bytes } from "./Bytes.sol"; + +/** + * TODO: add access control + */ +contract World is StoreView { + error World_InvalidSystem(); + + function registerSchema(bytes32 table, SchemaType[] memory schema) public override { + StoreCore.registerSchema(table, schema); + } + + function setData( + bytes32 table, + bytes32[] memory key, + bytes memory data + ) public override { + StoreCore.setData(table, key, data); + } + + function setData( + bytes32 table, + bytes32[] memory key, + uint8 schemaIndex, + bytes memory data + ) public override { + StoreCore.setData(table, key, schemaIndex, data); + } + + function registerSystem( + address contractAddress, + string memory contractName, + string memory functionSig, + ExecutionMode executionMode + ) public { + // TODO: checks + bytes4 worldSelector = bytes4(keccak256(abi.encodePacked(contractName, "_", functionSig))); + bytes4 funcSelector = bytes4(keccak256(abi.encodePacked(functionSig))); + SystemTable.set(bytes32(worldSelector), contractAddress, funcSelector, uint8(executionMode)); + } + + fallback() external payable { + // Find system by generated function selector + SystemTableEntry memory system = SystemTable.get(bytes32(msg.sig)); + + address systemAddress = system.addr; + bytes4 systemSelector = system.selector; + + if (system.addr == address(0)) revert World_InvalidSystem(); + + // Call the system function via `call` if the system is autonomous + if (system.executionMode == uint8(ExecutionMode.Autonomous)) { + assembly { + // place system function selector at memory position 0 + mstore(0, systemSelector) + + // place existing calldata (exclusing selector) after system function selector + calldatacopy(4, 4, sub(calldatasize(), 4)) + + // place msg.sender after calldata + mstore(calldatasize(), caller()) + + // execute function call using the system and pass the constructed calldata + let result := call(gas(), systemAddress, callvalue(), 0, add(calldatasize(), 32), 0, 0) + + // place any return value into memory at position 0 + returndatacopy(0, 0, returndatasize()) + + // return any return value or error back to the caller + switch result + case 0 { + revert(0, returndatasize()) + } + default { + return(0, returndatasize()) + } + } + } + + // Call the system function via `delegatecall` if the system is autonomous + if (system.executionMode == uint8(ExecutionMode.Delegate)) { + assembly { + // place system function selector at memory position 0 + mstore(0, systemSelector) + + // place existing calldata (exclusing selector) after system function selector + calldatacopy(4, 4, sub(calldatasize(), 4)) + + // place msg.sender after calldata + mstore(calldatasize(), caller()) + + // execute function call using the system and pass the constructed calldata + let result := delegatecall(gas(), systemAddress, 0, add(calldatasize(), 32), 0, 0) + + // place any return value into memory at position 0 + returndatacopy(0, 0, returndatasize()) + + // return any return value or error back to the caller + switch result + case 0 { + revert(0, returndatasize()) + } + default { + return(0, returndatasize()) + } + } + } + } +} diff --git a/packages/solecs/v2/tables/SystemTable.sol b/packages/solecs/v2/tables/SystemTable.sol index b937a8414c..171eab36ff 100644 --- a/packages/solecs/v2/tables/SystemTable.sol +++ b/packages/solecs/v2/tables/SystemTable.sol @@ -2,6 +2,7 @@ pragma solidity >=0.8.0; import { console } from "forge-std/console.sol"; +import { IStore } from "../IStore.sol"; import { StoreSwitch } from "../StoreSwitch.sol"; import { StoreCore } from "../StoreCore.sol"; import { SchemaType } from "../Types.sol"; @@ -34,6 +35,10 @@ library SystemTable { StoreSwitch.registerSchema(id, getSchema()); } + function registerSchema(IStore store) internal { + store.registerSchema(id, getSchema()); + } + /** Set the table's data */ function set( bytes32 key, @@ -74,6 +79,17 @@ library SystemTable { bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = key; bytes memory blob = StoreSwitch.getData(id, keyTuple, 25); + return decode(blob); + } + + function get(IStore store, bytes32 key) internal view returns (Schema memory data) { + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = key; + bytes memory blob = store.getData(id, keyTuple, 25); + return decode(blob); + } + + function decode(bytes memory blob) internal pure returns (Schema memory data) { return Schema({ addr: Bytes.toAddress(Bytes.slice(blob, 0, 20)), diff --git a/packages/solecs/v2/tables/Vec2Table.sol b/packages/solecs/v2/tables/Vec2Table.sol index e3a9a7210d..11af8c8918 100644 --- a/packages/solecs/v2/tables/Vec2Table.sol +++ b/packages/solecs/v2/tables/Vec2Table.sol @@ -2,6 +2,7 @@ pragma solidity >=0.8.0; import { console } from "forge-std/console.sol"; +import { IStore } from "../IStore.sol"; import { StoreSwitch } from "../StoreSwitch.sol"; import { StoreCore } from "../StoreCore.sol"; import { SchemaType } from "../Types.sol"; @@ -32,6 +33,10 @@ library Vec2Table { StoreSwitch.registerSchema(id, getSchema()); } + function registerSchema(IStore store) internal { + store.registerSchema(id, getSchema()); + } + /** Set the table's data */ function set( bytes32 key, @@ -65,6 +70,17 @@ library Vec2Table { bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = key; bytes memory blob = StoreSwitch.getData(id, keyTuple, 8); + return decode(blob); + } + + function get(IStore store, bytes32 key) internal view returns (Schema memory vec2) { + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = key; + bytes memory blob = store.getData(id, keyTuple, 8); + return decode(blob); + } + + function decode(bytes memory blob) internal pure returns (Schema memory vec2) { return Schema({ x: Bytes.toUint32(Bytes.slice(blob, 0, 4)), y: Bytes.toUint32(Bytes.slice(blob, 4, 4)) }); // Alternative approach, but more expensive: diff --git a/packages/solecs/v2/test/System.t.sol b/packages/solecs/v2/test/System.t.sol new file mode 100644 index 0000000000..c2c1a131bd --- /dev/null +++ b/packages/solecs/v2/test/System.t.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import { console } from "forge-std/console.sol"; +import { DSTestPlus } from "solmate/test/utils/DSTestPlus.sol"; +import { World } from "../World.sol"; +import { System } from "../System.sol"; + +contract TestSystem is System { + function msgSender() public pure returns (address) { + return _msgSender(); + } +} + +contract SystemTest is DSTestPlus { + function testMsgSender() public { + TestSystem system = new TestSystem(); + address sender = address(0x123); + (bool success, bytes memory returndata) = address(system).call(abi.encodeWithSignature("msgSender()", sender)); + assertTrue(success); + assertEq(abi.decode(returndata, (address)), sender); + } +} diff --git a/packages/solecs/v2/test/World.t.sol b/packages/solecs/v2/test/World.t.sol new file mode 100644 index 0000000000..64efcadd06 --- /dev/null +++ b/packages/solecs/v2/test/World.t.sol @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import { console } from "forge-std/console.sol"; +import { DSTestPlus } from "solmate/test/utils/DSTestPlus.sol"; +import { World } from "../World.sol"; +import { System } from "../System.sol"; +import { ExecutionMode } from "../Types.sol"; +import { Vec2Table, Schema as Vec2 } from "../tables/Vec2Table.sol"; + +contract TestSystem is System { + function msgSender() public pure returns (address) { + return _msgSender(); + } + + // TestSystem's move function sets state on the Vec2Table of the caller + // (independent of delegatecall or regular call) + function move( + bytes32 entity, + uint32 x, + uint32 y + ) public { + Vec2Table.set({ key: entity, x: x, y: y }); + } +} + +// TODO: auto-generate this interface from registered systems +interface WorldWithTestSystem { + function TestSystem_msgSender() external view returns (address); + + function TestSystem_move( + bytes32 entity, + uint32 x, + uint32 y + ) external; +} + +contract WorldTest is DSTestPlus { + World internal world; + TestSystem internal system; + + function setUp() public { + world = new World(); + system = new TestSystem(); + } + + function _registerSystem(ExecutionMode executionMode) internal {} + + function testRegisterAndCallSystem() public { + // Register autonomous system in the world + world.registerSystem(address(system), "TestSystem", "msgSender()", ExecutionMode.Autonomous); + + // Call system via world contract + address msgSender = WorldWithTestSystem(address(world)).TestSystem_msgSender(); + + // msg.sender (this) should have been passed to system + assertEq(msgSender, address(this)); + } + + function testRegisterAndDelegatecallSystem() public { + // Register delegate system in the world + world.registerSystem(address(system), "TestSystem", "msgSender()", ExecutionMode.Delegate); + + // Call system via world contract + address msgSender = WorldWithTestSystem(address(world)).TestSystem_msgSender(); + + // msg.sender (this) should have been passed to system + assertEq(msgSender, address(this)); + } + + function testRegisterVec2Table() public { + // Register table + Vec2Table.registerSchema(world); + } + + // Register TestSystem as autonomous system and call its move function to set state on Vec2Table + function testSetVec2ViaTestSystemAutonomous() public { + // Register autonomous system in the world + world.registerSystem(address(system), "TestSystem", "move(bytes32,uint32,uint32)", ExecutionMode.Autonomous); + + // Register table + Vec2Table.registerSchema(world); + + // Call autonomous system's move function via world contract + bytes32 entity = keccak256("entity"); + WorldWithTestSystem(address(world)).TestSystem_move(entity, 1, 2); + + // Get state from the table (using out-of-system syntax) + Vec2 memory vec2 = Vec2Table.get(world, entity); + + // Verify the state has been set correctly + assertEq(vec2.x, 1); + assertEq(vec2.y, 2); + } + + // Register TestSystem as delegate system and call its move function to set state on Vec2Table + function testSetVec2ViaTestSystemDelegate() public { + // Register delegate system in the world + world.registerSystem(address(system), "TestSystem", "move(bytes32,uint32,uint32)", ExecutionMode.Delegate); + + // Register table + Vec2Table.registerSchema(world); + + // Call delegate system's move function via world contract + bytes32 entity = keccak256("entity"); + WorldWithTestSystem(address(world)).TestSystem_move(entity, 1, 2); + + // Get state from the table (using out-of-system syntax) + Vec2 memory vec2 = Vec2Table.get(world, entity); + + // Verify the state has been set correctly + assertEq(vec2.x, 1); + assertEq(vec2.y, 2); + } +} From 4c249d81fe86ea90bd0feb9f9bb494e170cfd7df Mon Sep 17 00:00:00 2001 From: alvrs Date: Fri, 20 Jan 2023 11:02:27 +0100 Subject: [PATCH 08/82] refactor(solecs): wip data model - rename schemas and tables --- packages/solecs/v2/World.sol | 24 +++++++------- .../{SystemTable.sol => RouteTable.sol} | 16 +++++----- .../{Vec2Table.sol => Vector2Table.sol} | 16 +++++----- .../{SystemTable.t.sol => RouteTable.t.sol} | 14 ++++---- .../{Vec2Table.t.sol => Vector2Table.t.sol} | 20 ++++++------ packages/solecs/v2/test/World.t.sol | 32 +++++++++++-------- 6 files changed, 64 insertions(+), 58 deletions(-) rename packages/solecs/v2/tables/{SystemTable.sol => RouteTable.sol} (88%) rename packages/solecs/v2/tables/{Vec2Table.sol => Vector2Table.sol} (83%) rename packages/solecs/v2/test/{SystemTable.t.sol => RouteTable.t.sol} (72%) rename packages/solecs/v2/test/{Vec2Table.t.sol => Vector2Table.t.sol} (67%) diff --git a/packages/solecs/v2/World.sol b/packages/solecs/v2/World.sol index 694b41bf24..2f1cdd14a8 100644 --- a/packages/solecs/v2/World.sol +++ b/packages/solecs/v2/World.sol @@ -5,7 +5,7 @@ import { console } from "forge-std/console.sol"; import { StoreCore } from "./StoreCore.sol"; import { StoreView } from "./StoreView.sol"; import { SchemaType, ExecutionMode } from "./Types.sol"; -import { SystemTable, Schema as SystemTableEntry } from "./tables/SystemTable.sol"; +import { RouteTable, Route } from "./tables/RouteTable.sol"; import { Bytes } from "./Bytes.sol"; /** @@ -44,23 +44,23 @@ contract World is StoreView { // TODO: checks bytes4 worldSelector = bytes4(keccak256(abi.encodePacked(contractName, "_", functionSig))); bytes4 funcSelector = bytes4(keccak256(abi.encodePacked(functionSig))); - SystemTable.set(bytes32(worldSelector), contractAddress, funcSelector, uint8(executionMode)); + RouteTable.set(bytes32(worldSelector), contractAddress, funcSelector, uint8(executionMode)); } fallback() external payable { // Find system by generated function selector - SystemTableEntry memory system = SystemTable.get(bytes32(msg.sig)); + Route memory route = RouteTable.get(bytes32(msg.sig)); - address systemAddress = system.addr; - bytes4 systemSelector = system.selector; + address addr = route.addr; + bytes4 selector = route.selector; - if (system.addr == address(0)) revert World_InvalidSystem(); + if (addr == address(0)) revert World_InvalidSystem(); // Call the system function via `call` if the system is autonomous - if (system.executionMode == uint8(ExecutionMode.Autonomous)) { + if (route.executionMode == uint8(ExecutionMode.Autonomous)) { assembly { // place system function selector at memory position 0 - mstore(0, systemSelector) + mstore(0, selector) // place existing calldata (exclusing selector) after system function selector calldatacopy(4, 4, sub(calldatasize(), 4)) @@ -69,7 +69,7 @@ contract World is StoreView { mstore(calldatasize(), caller()) // execute function call using the system and pass the constructed calldata - let result := call(gas(), systemAddress, callvalue(), 0, add(calldatasize(), 32), 0, 0) + let result := call(gas(), addr, callvalue(), 0, add(calldatasize(), 32), 0, 0) // place any return value into memory at position 0 returndatacopy(0, 0, returndatasize()) @@ -86,10 +86,10 @@ contract World is StoreView { } // Call the system function via `delegatecall` if the system is autonomous - if (system.executionMode == uint8(ExecutionMode.Delegate)) { + if (route.executionMode == uint8(ExecutionMode.Delegate)) { assembly { // place system function selector at memory position 0 - mstore(0, systemSelector) + mstore(0, selector) // place existing calldata (exclusing selector) after system function selector calldatacopy(4, 4, sub(calldatasize(), 4)) @@ -98,7 +98,7 @@ contract World is StoreView { mstore(calldatasize(), caller()) // execute function call using the system and pass the constructed calldata - let result := delegatecall(gas(), systemAddress, 0, add(calldatasize(), 32), 0, 0) + let result := delegatecall(gas(), addr, 0, add(calldatasize(), 32), 0, 0) // place any return value into memory at position 0 returndatacopy(0, 0, returndatasize()) diff --git a/packages/solecs/v2/tables/SystemTable.sol b/packages/solecs/v2/tables/RouteTable.sol similarity index 88% rename from packages/solecs/v2/tables/SystemTable.sol rename to packages/solecs/v2/tables/RouteTable.sol index 171eab36ff..14318a57c3 100644 --- a/packages/solecs/v2/tables/SystemTable.sol +++ b/packages/solecs/v2/tables/RouteTable.sol @@ -10,9 +10,9 @@ import { Bytes } from "../Bytes.sol"; // -- User defined schema and id -- -bytes32 constant id = keccak256("mud.store.table.system"); +bytes32 constant id = keccak256("mud.store.table.route"); -struct Schema { +struct Route { address addr; bytes4 selector; uint8 executionMode; @@ -21,7 +21,7 @@ struct Schema { // -- Autogenerated schema and library -- // TODO: autogenerate -library SystemTable { +library RouteTable { /** Get the table's schema */ function getSchema() internal pure returns (SchemaType[] memory schema) { schema = new SchemaType[](3); @@ -52,7 +52,7 @@ library SystemTable { StoreSwitch.setData(id, keyTuple, data); } - function set(bytes32 key, Schema memory data) internal { + function set(bytes32 key, Route memory data) internal { set(key, data.addr, data.selector, data.executionMode); } @@ -75,23 +75,23 @@ library SystemTable { } /** Get the table's data */ - function get(bytes32 key) internal view returns (Schema memory data) { + function get(bytes32 key) internal view returns (Route memory data) { bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = key; bytes memory blob = StoreSwitch.getData(id, keyTuple, 25); return decode(blob); } - function get(IStore store, bytes32 key) internal view returns (Schema memory data) { + function get(IStore store, bytes32 key) internal view returns (Route memory data) { bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = key; bytes memory blob = store.getData(id, keyTuple, 25); return decode(blob); } - function decode(bytes memory blob) internal pure returns (Schema memory data) { + function decode(bytes memory blob) internal pure returns (Route memory data) { return - Schema({ + Route({ addr: Bytes.toAddress(Bytes.slice(blob, 0, 20)), selector: Bytes.toBytes4(Bytes.slice(blob, 20, 4)), executionMode: Bytes.toUint8(Bytes.slice(blob, 24, 1)) diff --git a/packages/solecs/v2/tables/Vec2Table.sol b/packages/solecs/v2/tables/Vector2Table.sol similarity index 83% rename from packages/solecs/v2/tables/Vec2Table.sol rename to packages/solecs/v2/tables/Vector2Table.sol index 11af8c8918..32b7bd6611 100644 --- a/packages/solecs/v2/tables/Vec2Table.sol +++ b/packages/solecs/v2/tables/Vector2Table.sol @@ -10,9 +10,9 @@ import { Bytes } from "../Bytes.sol"; // -- User defined schema and id -- -bytes32 constant id = keccak256("mud.store.table.vec2"); +bytes32 constant id = keccak256("mud.store.table.vector3"); -struct Schema { +struct Vector2 { uint32 x; uint32 y; } @@ -20,7 +20,7 @@ struct Schema { // -- Autogenerated schema and library -- // TODO: autogenerate -library Vec2Table { +library Vector2Table { /** Get the table's schema */ function getSchema() internal pure returns (SchemaType[] memory schema) { schema = new SchemaType[](2); @@ -49,7 +49,7 @@ library Vec2Table { StoreSwitch.setData(id, keyTuple, data); } - function set(bytes32 key, Schema memory vec2) internal { + function set(bytes32 key, Vector2 memory vec2) internal { set(key, vec2.x, vec2.y); } @@ -66,22 +66,22 @@ library Vec2Table { } /** Get the table's data */ - function get(bytes32 key) internal view returns (Schema memory vec2) { + function get(bytes32 key) internal view returns (Vector2 memory vec2) { bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = key; bytes memory blob = StoreSwitch.getData(id, keyTuple, 8); return decode(blob); } - function get(IStore store, bytes32 key) internal view returns (Schema memory vec2) { + function get(IStore store, bytes32 key) internal view returns (Vector2 memory vec2) { bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = key; bytes memory blob = store.getData(id, keyTuple, 8); return decode(blob); } - function decode(bytes memory blob) internal pure returns (Schema memory vec2) { - return Schema({ x: Bytes.toUint32(Bytes.slice(blob, 0, 4)), y: Bytes.toUint32(Bytes.slice(blob, 4, 4)) }); + function decode(bytes memory blob) internal pure returns (Vector2 memory vec2) { + return Vector2({ x: Bytes.toUint32(Bytes.slice(blob, 0, 4)), y: Bytes.toUint32(Bytes.slice(blob, 4, 4)) }); // Alternative approach, but more expensive: // bytes[] memory data = StoreCore.split(blob, getSchema()); diff --git a/packages/solecs/v2/test/SystemTable.t.sol b/packages/solecs/v2/test/RouteTable.t.sol similarity index 72% rename from packages/solecs/v2/test/SystemTable.t.sol rename to packages/solecs/v2/test/RouteTable.t.sol index fa2b9744f4..9533655e2c 100644 --- a/packages/solecs/v2/test/SystemTable.t.sol +++ b/packages/solecs/v2/test/RouteTable.t.sol @@ -3,7 +3,7 @@ pragma solidity >=0.8.0; import { console } from "forge-std/console.sol"; import { DSTestPlus } from "solmate/test/utils/DSTestPlus.sol"; -import { SystemTable, id as SystemTableId, Schema as SystemEntry } from "../tables/SystemTable.sol"; +import { RouteTable, id as RouteTableId, Route } from "../tables/RouteTable.sol"; import { StoreCore } from "../StoreCore.sol"; import { SchemaType } from "../Types.sol"; import { StoreView } from "../StoreView.sol"; @@ -11,18 +11,18 @@ import { StoreView } from "../StoreView.sol"; contract SystemTableTest is DSTestPlus, StoreView { function testRegisterAndGetSchema() public { uint256 gas = gasleft(); - SystemTable.registerSchema(); + RouteTable.registerSchema(); gas = gas - gasleft(); console.log("gas used: %s", gas); - SchemaType[] memory registeredSchema = StoreCore.getSchema(SystemTableId); - SchemaType[] memory declaredSchema = SystemTable.getSchema(); + SchemaType[] memory registeredSchema = StoreCore.getSchema(RouteTableId); + SchemaType[] memory declaredSchema = RouteTable.getSchema(); assertEq(keccak256(abi.encode(registeredSchema)), keccak256(abi.encode(declaredSchema))); } function testSetAndGet() public { - SystemTable.registerSchema(); + RouteTable.registerSchema(); bytes32 key = keccak256("somekey"); address addr = address(0x1234); @@ -30,12 +30,12 @@ contract SystemTableTest is DSTestPlus, StoreView { uint8 executionMode = 1; uint256 gas = gasleft(); - SystemTable.set(key, addr, selector, executionMode); + RouteTable.set(key, addr, selector, executionMode); gas = gas - gasleft(); console.log("gas used (set): %s", gas); gas = gasleft(); - SystemEntry memory systemEntry = SystemTable.get(key); + Route memory systemEntry = RouteTable.get(key); gas = gas - gasleft(); console.log("gas used (get, warm): %s", gas); diff --git a/packages/solecs/v2/test/Vec2Table.t.sol b/packages/solecs/v2/test/Vector2Table.t.sol similarity index 67% rename from packages/solecs/v2/test/Vec2Table.t.sol rename to packages/solecs/v2/test/Vector2Table.t.sol index 5eff69ed00..02ff239b2e 100644 --- a/packages/solecs/v2/test/Vec2Table.t.sol +++ b/packages/solecs/v2/test/Vector2Table.t.sol @@ -3,39 +3,39 @@ pragma solidity >=0.8.0; import { console } from "forge-std/console.sol"; import { DSTestPlus } from "solmate/test/utils/DSTestPlus.sol"; -import { Vec2Table, id as Vec2Id, Schema as Vec2 } from "../tables/Vec2Table.sol"; +import { Vector2Table, id as Vector2Id, Vector2 } from "../tables/Vector2Table.sol"; import { StoreCore } from "../StoreCore.sol"; import { SchemaType } from "../Types.sol"; import { StoreView } from "../StoreView.sol"; -contract Vec2TableTest is DSTestPlus, StoreView { +contract Vector2TableTest is DSTestPlus, StoreView { function testRegisterAndGetSchema() public { uint256 gas = gasleft(); - Vec2Table.registerSchema(); + Vector2Table.registerSchema(); gas = gas - gasleft(); console.log("gas used: %s", gas); - SchemaType[] memory registeredSchema = StoreCore.getSchema(Vec2Id); - SchemaType[] memory declaredSchema = Vec2Table.getSchema(); + SchemaType[] memory registeredSchema = StoreCore.getSchema(Vector2Id); + SchemaType[] memory declaredSchema = Vector2Table.getSchema(); assertEq(keccak256(abi.encode(registeredSchema)), keccak256(abi.encode(declaredSchema))); } function testSetAndGet() public { - Vec2Table.registerSchema(); + Vector2Table.registerSchema(); bytes32 key = keccak256("somekey"); uint256 gas = gasleft(); - Vec2Table.set({ key: key, x: 1, y: 2 }); + Vector2Table.set({ key: key, x: 1, y: 2 }); gas = gas - gasleft(); console.log("gas used (set): %s", gas); gas = gasleft(); - Vec2 memory vec2 = Vec2Table.get(key); + Vector2 memory vector = Vector2Table.get(key); gas = gas - gasleft(); console.log("gas used (get, warm): %s", gas); - assertEq(vec2.x, 1); - assertEq(vec2.y, 2); + assertEq(vector.x, 1); + assertEq(vector.y, 2); } } diff --git a/packages/solecs/v2/test/World.t.sol b/packages/solecs/v2/test/World.t.sol index 64efcadd06..d079d67e4d 100644 --- a/packages/solecs/v2/test/World.t.sol +++ b/packages/solecs/v2/test/World.t.sol @@ -6,21 +6,21 @@ import { DSTestPlus } from "solmate/test/utils/DSTestPlus.sol"; import { World } from "../World.sol"; import { System } from "../System.sol"; import { ExecutionMode } from "../Types.sol"; -import { Vec2Table, Schema as Vec2 } from "../tables/Vec2Table.sol"; +import { Vector2Table, Vector2 } from "../tables/Vector2Table.sol"; contract TestSystem is System { function msgSender() public pure returns (address) { return _msgSender(); } - // TestSystem's move function sets state on the Vec2Table of the caller + // TestSystem's move function sets state on the Vector2Table of the caller // (independent of delegatecall or regular call) function move( bytes32 entity, uint32 x, uint32 y ) public { - Vec2Table.set({ key: entity, x: x, y: y }); + Vector2Table.set({ key: entity, x: x, y: y }); } } @@ -68,45 +68,51 @@ contract WorldTest is DSTestPlus { assertEq(msgSender, address(this)); } - function testRegisterVec2Table() public { + function testRegisterVector2Table() public { // Register table - Vec2Table.registerSchema(world); + Vector2Table.registerSchema(world); } - // Register TestSystem as autonomous system and call its move function to set state on Vec2Table - function testSetVec2ViaTestSystemAutonomous() public { + // Register TestSystem as autonomous system and call its move function to set state on Vector2Table + function testSetVector2ViaTestSystemAutonomous() public { // Register autonomous system in the world world.registerSystem(address(system), "TestSystem", "move(bytes32,uint32,uint32)", ExecutionMode.Autonomous); // Register table - Vec2Table.registerSchema(world); + Vector2Table.registerSchema(world); // Call autonomous system's move function via world contract bytes32 entity = keccak256("entity"); + uint256 gas = gasleft(); WorldWithTestSystem(address(world)).TestSystem_move(entity, 1, 2); + gas = gas - gasleft(); + console.log("gas used (autonomous system): ", gas); // Get state from the table (using out-of-system syntax) - Vec2 memory vec2 = Vec2Table.get(world, entity); + Vector2 memory vec2 = Vector2Table.get(world, entity); // Verify the state has been set correctly assertEq(vec2.x, 1); assertEq(vec2.y, 2); } - // Register TestSystem as delegate system and call its move function to set state on Vec2Table - function testSetVec2ViaTestSystemDelegate() public { + // Register TestSystem as delegate system and call its move function to set state on Vector2Table + function testSetVector2ViaTestSystemDelegate() public { // Register delegate system in the world world.registerSystem(address(system), "TestSystem", "move(bytes32,uint32,uint32)", ExecutionMode.Delegate); // Register table - Vec2Table.registerSchema(world); + Vector2Table.registerSchema(world); // Call delegate system's move function via world contract bytes32 entity = keccak256("entity"); + uint256 gas = gasleft(); WorldWithTestSystem(address(world)).TestSystem_move(entity, 1, 2); + gas = gas - gasleft(); + console.log("gas used (delegate system): ", gas); // Get state from the table (using out-of-system syntax) - Vec2 memory vec2 = Vec2Table.get(world, entity); + Vector2 memory vec2 = Vector2Table.get(world, entity); // Verify the state has been set correctly assertEq(vec2.x, 1); From c358c8367c637fea5c8d2fbc900c4496518de6c0 Mon Sep 17 00:00:00 2001 From: alvrs Date: Fri, 20 Jan 2023 11:33:59 +0100 Subject: [PATCH 09/82] refactor(solecs): wip data model - convert bytes without copying --- packages/solecs/v2/Bytes.sol | 296 ++++++++++++++++++++- packages/solecs/v2/tables/RouteTable.sol | 6 +- packages/solecs/v2/tables/Vector2Table.sol | 5 +- packages/solecs/v2/test/Bytes.t.sol | 32 +++ 4 files changed, 334 insertions(+), 5 deletions(-) diff --git a/packages/solecs/v2/Bytes.sol b/packages/solecs/v2/Bytes.sol index 4742b55398..db18189ed1 100644 --- a/packages/solecs/v2/Bytes.sol +++ b/packages/solecs/v2/Bytes.sol @@ -122,7 +122,7 @@ library Bytes { /************************************************************************ * - * BYTES UTILS + * UTILS * ************************************************************************/ @@ -140,6 +140,12 @@ library Bytes { return input; } + /************************************************************************ + * + * SLICE + * + ************************************************************************/ + // From https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol function slice( bytes memory _bytes, @@ -206,6 +212,294 @@ library Bytes { return tempBytes; } + /** Slice bytes to bytes1 without copying data */ + function slice1(bytes memory data, uint256 start) internal pure returns (bytes1) { + bytes1 output; + assembly { + output := mload(add(add(data, 0x20), start)) + } + return output; + } + + /** Slice bytes to bytes2 without copying data */ + function slice2(bytes memory data, uint256 start) internal pure returns (bytes2) { + bytes2 output; + assembly { + output := mload(add(add(data, 0x20), start)) + } + return output; + } + + /** Slice bytes to bytes3 without copying data */ + function slice3(bytes memory data, uint256 start) internal pure returns (bytes3) { + bytes3 output; + assembly { + output := mload(add(add(data, 0x20), start)) + } + return output; + } + + /** Slice bytes to bytes4 without copying data */ + function slice4(bytes memory data, uint256 start) internal pure returns (bytes4) { + bytes4 output; + assembly { + output := mload(add(add(data, 0x20), start)) + } + return output; + } + + /** Slice bytes to bytes5 without copying data */ + function slice5(bytes memory data, uint256 start) internal pure returns (bytes5) { + bytes5 output; + assembly { + output := mload(add(add(data, 0x20), start)) + } + return output; + } + + /** Slice bytes to bytes6 without copying data */ + function slice6(bytes memory data, uint256 start) internal pure returns (bytes6) { + bytes6 output; + assembly { + output := mload(add(add(data, 0x20), start)) + } + return output; + } + + /** Slice bytes to bytes7 without copying data */ + function slice7(bytes memory data, uint256 start) internal pure returns (bytes7) { + bytes7 output; + assembly { + output := mload(add(add(data, 0x20), start)) + } + return output; + } + + /** Slice bytes to bytes8 without copying data */ + function slice8(bytes memory data, uint256 start) internal pure returns (bytes8) { + bytes8 output; + assembly { + output := mload(add(add(data, 0x20), start)) + } + return output; + } + + /** Slice bytes to bytes9 without copying data */ + function slice9(bytes memory data, uint256 start) internal pure returns (bytes9) { + bytes9 output; + assembly { + output := mload(add(add(data, 0x20), start)) + } + return output; + } + + /** Slice bytes to bytes10 without copying data */ + function slice10(bytes memory data, uint256 start) internal pure returns (bytes10) { + bytes10 output; + assembly { + output := mload(add(add(data, 0x20), start)) + } + return output; + } + + /** Slice bytes to bytes11 without copying data */ + function slice11(bytes memory data, uint256 start) internal pure returns (bytes11) { + bytes11 output; + assembly { + output := mload(add(add(data, 0x20), start)) + } + return output; + } + + /** Slice bytes to bytes12 without copying data */ + function slice12(bytes memory data, uint256 start) internal pure returns (bytes12) { + bytes12 output; + assembly { + output := mload(add(add(data, 0x20), start)) + } + return output; + } + + /** Slice bytes to bytes13 without copying data */ + function slice13(bytes memory data, uint256 start) internal pure returns (bytes13) { + bytes13 output; + assembly { + output := mload(add(add(data, 0x20), start)) + } + return output; + } + + /** Slice bytes to bytes14 without copying data */ + function slice14(bytes memory data, uint256 start) internal pure returns (bytes14) { + bytes14 output; + assembly { + output := mload(add(add(data, 0x20), start)) + } + return output; + } + + /** Slice bytes to bytes15 without copying data */ + function slice15(bytes memory data, uint256 start) internal pure returns (bytes15) { + bytes15 output; + assembly { + output := mload(add(add(data, 0x20), start)) + } + return output; + } + + /** Slice bytes to bytes16 without copying data */ + function slice16(bytes memory data, uint256 start) internal pure returns (bytes16) { + bytes16 output; + assembly { + output := mload(add(add(data, 0x20), start)) + } + return output; + } + + /** Slice bytes to bytes17 without copying data */ + function slice17(bytes memory data, uint256 start) internal pure returns (bytes17) { + bytes17 output; + assembly { + output := mload(add(add(data, 0x20), start)) + } + return output; + } + + /** Slice bytes to bytes18 without copying data */ + function slice18(bytes memory data, uint256 start) internal pure returns (bytes18) { + bytes18 output; + assembly { + output := mload(add(add(data, 0x20), start)) + } + return output; + } + + /** Slice bytes to bytes19 without copying data */ + function slice19(bytes memory data, uint256 start) internal pure returns (bytes19) { + bytes19 output; + assembly { + output := mload(add(add(data, 0x20), start)) + } + return output; + } + + /** Slice bytes to bytes20 without copying data */ + function slice20(bytes memory data, uint256 start) internal pure returns (bytes20) { + bytes20 output; + assembly { + output := mload(add(add(data, 0x20), start)) + } + return output; + } + + /** Slice bytes to bytes21 without copying data */ + function slice21(bytes memory data, uint256 start) internal pure returns (bytes21) { + bytes21 output; + assembly { + output := mload(add(add(data, 0x20), start)) + } + return output; + } + + /** Slice bytes to bytes22 without copying data */ + function slice22(bytes memory data, uint256 start) internal pure returns (bytes22) { + bytes22 output; + assembly { + output := mload(add(add(data, 0x20), start)) + } + return output; + } + + /** Slice bytes to bytes23 without copying data */ + function slice23(bytes memory data, uint256 start) internal pure returns (bytes23) { + bytes23 output; + assembly { + output := mload(add(add(data, 0x20), start)) + } + return output; + } + + /** Slice bytes to bytes24 without copying data */ + function slice24(bytes memory data, uint256 start) internal pure returns (bytes24) { + bytes24 output; + assembly { + output := mload(add(add(data, 0x20), start)) + } + return output; + } + + /** Slice bytes to bytes25 without copying data */ + function slice25(bytes memory data, uint256 start) internal pure returns (bytes25) { + bytes25 output; + assembly { + output := mload(add(add(data, 0x20), start)) + } + return output; + } + + /** Slice bytes to bytes26 without copying data */ + function slice26(bytes memory data, uint256 start) internal pure returns (bytes26) { + bytes26 output; + assembly { + output := mload(add(add(data, 0x20), start)) + } + return output; + } + + /** Slice bytes to bytes27 without copying data */ + function slice27(bytes memory data, uint256 start) internal pure returns (bytes27) { + bytes27 output; + assembly { + output := mload(add(add(data, 0x20), start)) + } + return output; + } + + /** Slice bytes to bytes28 without copying data */ + function slice28(bytes memory data, uint256 start) internal pure returns (bytes28) { + bytes28 output; + assembly { + output := mload(add(add(data, 0x20), start)) + } + return output; + } + + /** Slice bytes to bytes29 without copying data */ + function slice29(bytes memory data, uint256 start) internal pure returns (bytes29) { + bytes29 output; + assembly { + output := mload(add(add(data, 0x20), start)) + } + return output; + } + + /** Slice bytes to bytes30 without copying data */ + function slice30(bytes memory data, uint256 start) internal pure returns (bytes30) { + bytes30 output; + assembly { + output := mload(add(add(data, 0x20), start)) + } + return output; + } + + /** Slice bytes to bytes31 without copying data */ + function slice31(bytes memory data, uint256 start) internal pure returns (bytes31) { + bytes31 output; + assembly { + output := mload(add(add(data, 0x20), start)) + } + return output; + } + + /** Slice bytes to bytes32 without copying data */ + function slice32(bytes memory data, uint256 start) internal pure returns (bytes32) { + bytes32 output; + assembly { + output := mload(add(add(data, 0x20), start)) + } + return output; + } + /** * Splits a `bytes` memory array into a `bytes` memory array of the given lengths. */ diff --git a/packages/solecs/v2/tables/RouteTable.sol b/packages/solecs/v2/tables/RouteTable.sol index 14318a57c3..7820f1514f 100644 --- a/packages/solecs/v2/tables/RouteTable.sol +++ b/packages/solecs/v2/tables/RouteTable.sol @@ -92,9 +92,9 @@ library RouteTable { function decode(bytes memory blob) internal pure returns (Route memory data) { return Route({ - addr: Bytes.toAddress(Bytes.slice(blob, 0, 20)), - selector: Bytes.toBytes4(Bytes.slice(blob, 20, 4)), - executionMode: Bytes.toUint8(Bytes.slice(blob, 24, 1)) + addr: address(Bytes.slice20(blob, 0)), + selector: Bytes.slice4(blob, 20), + executionMode: uint8(Bytes.slice1(blob, 24)) }); } } diff --git a/packages/solecs/v2/tables/Vector2Table.sol b/packages/solecs/v2/tables/Vector2Table.sol index 32b7bd6611..e3e518949b 100644 --- a/packages/solecs/v2/tables/Vector2Table.sol +++ b/packages/solecs/v2/tables/Vector2Table.sol @@ -81,9 +81,12 @@ library Vector2Table { } function decode(bytes memory blob) internal pure returns (Vector2 memory vec2) { - return Vector2({ x: Bytes.toUint32(Bytes.slice(blob, 0, 4)), y: Bytes.toUint32(Bytes.slice(blob, 4, 4)) }); + return Vector2({ x: uint32(Bytes.slice4(blob, 0)), y: uint32(Bytes.slice4(blob, 4)) }); // Alternative approach, but more expensive: + // return Vector2({ x: Bytes.toUint32(Bytes.slice(blob, 0, 4)), y: Bytes.toUint32(Bytes.slice(blob, 4, 4)) }); + + // Alternative approach, but even more expensive: // bytes[] memory data = StoreCore.split(blob, getSchema()); // return Schema({ x: Bytes.toUint32(data[0]), y: Bytes.toUint32(data[1]) }); } diff --git a/packages/solecs/v2/test/Bytes.t.sol b/packages/solecs/v2/test/Bytes.t.sol index a8638c2019..84c963a860 100644 --- a/packages/solecs/v2/test/Bytes.t.sol +++ b/packages/solecs/v2/test/Bytes.t.sol @@ -219,4 +219,36 @@ contract BytesTest is DSTestPlus { assertEq(uint256(uint8(b[1])), 0x03); assertEq(uint256(uint8(b[2])), 0x04); } + + // TODO: add tests for other sliceX functions + function testSlice3() public { + bytes memory a = new bytes(5); + a[0] = 0x01; + a[1] = 0x02; + a[2] = 0x03; + a[3] = 0x04; + a[4] = 0x05; + + uint256 gas = gasleft(); + bytes3 b = Bytes.slice3(a, 1); + gas = gas - gasleft(); + console.log("gas used: %s", gas); + + assertEq(b.length, 3); + assertEq(uint256(uint8(b[0])), 0x02); + assertEq(uint256(uint8(b[1])), 0x03); + assertEq(uint256(uint8(b[2])), 0x04); + } + + function testSlice32() public { + bytes32 original = keccak256("some data"); + bytes memory input = bytes.concat(bytes10(keccak256("irrelevant data")), original); + + uint256 gas = gasleft(); + bytes32 output = Bytes.slice32(input, 10); + gas = gas - gasleft(); + console.log("gas used: %s", gas); + + assertEq(output, original); + } } From 669562b6a1d2048a2feb6429c14380ad827331b7 Mon Sep 17 00:00:00 2001 From: alvrs Date: Fri, 20 Jan 2023 14:35:00 +0100 Subject: [PATCH 10/82] feat(solecs): wip data model - add more efficient packing methods --- packages/solecs/v2/Bytes.sol | 59 ++++++++++++++++++++++++++--- packages/solecs/v2/test/Bytes.t.sol | 49 ++++++++++++++++++++++++ 2 files changed, 102 insertions(+), 6 deletions(-) diff --git a/packages/solecs/v2/Bytes.sol b/packages/solecs/v2/Bytes.sol index db18189ed1..eea86577fa 100644 --- a/packages/solecs/v2/Bytes.sol +++ b/packages/solecs/v2/Bytes.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; +import { console } from "forge-std/console.sol"; import { Utils } from "./Utils.sol"; import { SchemaType } from "./Types.sol"; @@ -25,16 +26,62 @@ library Bytes { } function from(uint8[] memory input) internal pure returns (bytes memory output) { - output = new bytes(input.length); - for (uint256 i; i < input.length; i++) { - output[i] = bytes1(input[i]); + bytes32 ptr; + assembly { + ptr := input } + + return _from(ptr, 1); } function from(SchemaType[] memory input) internal pure returns (bytes memory output) { - output = new bytes(input.length); - for (uint256 i; i < input.length; i++) { - output[i] = bytes1(uint8(input[i])); + bytes32 ptr; + assembly { + ptr := input + } + + return _from(ptr, 1); + } + + function from(uint16[] memory input) internal pure returns (bytes memory output) { + // This implementation costs 500 gas per entry: + // output = new bytes(0); + // for (uint256 i; i < input.length; i++) { + // output = bytes.concat(output, bytes2(input[i])); + // } + + // This implementation costs around 100 gas per entry: + bytes32 ptr; + assembly { + ptr := input + } + + return _from(ptr, 2); + } + + function _from(bytes32 _ptr, uint256 _bytesPerElement) internal pure returns (bytes memory output) { + assembly { + let inputLength := mload(_ptr) + let outputBytes := mul(inputLength, _bytesPerElement) + + // Allocate memory for the output and store its length + output := mload(0x40) + mstore(output, outputBytes) + + // Update the free memory pointer + mstore(0x40, add(output, add(32, outputBytes))) + + let outputPtr := add(output, 32) + let shiftBits := mul(sub(32, _bytesPerElement), 8) + for { + let inputPtr := add(_ptr, 32) // Start at first element + // Stop at last element + } lt(inputPtr, add(add(_ptr, 32), mul(mload(_ptr), 32))) { + inputPtr := add(inputPtr, 32) // Go to next input element + outputPtr := add(outputPtr, _bytesPerElement) // Go to next output slot + } { + mstore(outputPtr, shl(shiftBits, mload(inputPtr))) // Store the value in minimal bytes + } } } diff --git a/packages/solecs/v2/test/Bytes.t.sol b/packages/solecs/v2/test/Bytes.t.sol index 84c963a860..acc34daa34 100644 --- a/packages/solecs/v2/test/Bytes.t.sol +++ b/packages/solecs/v2/test/Bytes.t.sol @@ -36,6 +36,24 @@ contract BytesTest is DSTestPlus { assertEq(uint256(uint8(output[1])), 0x02); } + function testFromUint16Array() public { + uint16[] memory input = new uint16[](3); + input[0] = 0x0102; + input[1] = 0x0304; + input[2] = 0x0506; + uint256 gas = gasleft(); + bytes memory output = Bytes.from(input); + gas = gas - gasleft(); + console.log("gas used: %s", gas); + assertEq(output.length, 6); + assertEq(uint256(uint8(output[0])), 0x01); + assertEq(uint256(uint8(output[1])), 0x02); + assertEq(uint256(uint8(output[2])), 0x03); + assertEq(uint256(uint8(output[3])), 0x04); + assertEq(uint256(uint8(output[4])), 0x05); + assertEq(uint256(uint8(output[5])), 0x06); + } + function testToBytes32() public { bytes memory input = new bytes(32); input[0] = 0x01; @@ -251,4 +269,35 @@ contract BytesTest is DSTestPlus { assertEq(output, original); } + + function testAbiEncoding() public view { + bytes memory test = bytes.concat(bytes2(0x0102)); + console.log("bytes2 raw length: %s", test.length); + console.log("bytes2 abi encoded length: %s", abi.encode(test).length); + + string memory test2 = "max length of a string in a word"; + console.log("string raw length: %s", bytes(test2).length); + console.log("string abi encoded length: %s", abi.encode(test2).length); + + bytes[] memory test3 = new bytes[](2); + test3[0] = bytes.concat(bytes1(0x01)); + test3[1] = bytes.concat(bytes2(0x0203)); + + console.log("bytes[] raw length: %s", Bytes.from(test3).length); + console.log("bytes[] abi encoded length: %s", abi.encode(test3).length); + } + + function testEncodeDecode() public { + uint16[] memory input1 = new uint16[](2); + input1[0] = 0x0102; + input1[1] = 0x0304; + uint8[] memory input2 = new uint8[](2); + input2[0] = 0x05; + input2[1] = 0x06; + + bytes32 lengths = bytes32(bytes.concat(bytes2(uint16(input1.length)), bytes2(uint16(input2.length)))); + assertEq(lengths, bytes32(bytes4(0x00020002))); + + bytes memory input = bytes.concat(lengths, Bytes.from(input1), Bytes.from(input2)); + } } From fe24aeb02a81d3cd2e8f43c63710ab774f4f1447 Mon Sep 17 00:00:00 2001 From: alvrs Date: Tue, 24 Jan 2023 15:29:07 +0100 Subject: [PATCH 11/82] refactor(solecs): wip data model - change function names and schema representation --- packages/solecs/v2/IStore.sol | 60 ++++++--- packages/solecs/v2/StoreCore.sol | 145 +++++++++++---------- packages/solecs/v2/StoreSwitch.sol | 80 +++++++++--- packages/solecs/v2/StoreView.sol | 71 +++++++--- packages/solecs/v2/World.sol | 18 ++- packages/solecs/v2/tables/RouteTable.sol | 27 ++-- packages/solecs/v2/tables/Vector2Table.sol | 19 +-- packages/solecs/v2/test/RouteTable.t.sol | 4 +- packages/solecs/v2/test/StoreCore.t.sol | 91 ++++++------- packages/solecs/v2/test/Vector2Table.t.sol | 4 +- packages/solecs/v2/test/World.t.sol | 2 - 11 files changed, 311 insertions(+), 210 deletions(-) diff --git a/packages/solecs/v2/IStore.sol b/packages/solecs/v2/IStore.sol index 38b3ce2975..10d4d9f1a0 100644 --- a/packages/solecs/v2/IStore.sol +++ b/packages/solecs/v2/IStore.sol @@ -5,42 +5,68 @@ import { SchemaType } from "./Types.sol"; interface IStore { // note: the preimage of the tuple of keys used to index is part of the event, so it can be used by indexers - event StoreUpdate(bytes32 table, bytes32[] key, uint8 schemaIndex, bytes[] data); + event StoreUpdate(bytes32 table, bytes32[] key, uint16 arrayIndex, uint8 fieldIndex, bytes data); - function registerSchema(bytes32 table, SchemaType[] memory schema) external; + function registerSchema(bytes32 table, bytes32 schema) external; - function getSchema(bytes32 table) external view returns (SchemaType[] memory schema); + function getSchema(bytes32 table) external view returns (bytes32 schema); - // Set full data - function setData( + // Set full record (including full array) + function set( bytes32 table, bytes32[] memory key, bytes memory data ) external; - // Set partial data - function setData( + // Set partial data at schema index + function setField( bytes32 table, bytes32[] memory key, - uint8 schemaIndex, + uint8 fieldIndex, bytes memory data ) external; - // Get full data - function getData(bytes32 table, bytes32[] memory key) external view returns (bytes memory); + // Set full record of a single item at a given array index + function setArrayIndex( + bytes32 table, + bytes32[] memory key, + uint16 arrayIndex, + bytes memory data + ) external; + + // Set partial data of a single item at a given array index + function setArrayIndexField( + bytes32 table, + bytes32[] memory key, + uint16 arrayIndex, + uint8 fieldIndex, + bytes memory data + ) external; + + // Get full record (including full array) + function get(bytes32 table, bytes32[] memory key) external view returns (bytes memory data); + + // Get partial data at schema index + function getField( + bytes32 table, + bytes32[] memory key, + uint8 fieldIndex + ) external view returns (bytes memory data); - function getData( + // Get full record of a single item at a given array index + function getArrayIndex( bytes32 table, bytes32[] memory key, - uint256 length - ) external view returns (bytes memory); + uint16 arrayIndex + ) external view returns (bytes memory data); - // Get partial data - function getPartialData( + // Get partial data of a single item at a given array index + function getArrayIndexField( bytes32 table, bytes32[] memory key, - uint8 schemaIndex - ) external view returns (bytes memory); + uint16 arrayIndex, + uint8 fieldIndex + ) external view returns (bytes memory data); // If this function exists on the contract, it is a store // TODO: benchmark this vs. using a known storage slot to determine whether a contract is a Store diff --git a/packages/solecs/v2/StoreCore.sol b/packages/solecs/v2/StoreCore.sol index 9517929c9c..11d9305ede 100644 --- a/packages/solecs/v2/StoreCore.sol +++ b/packages/solecs/v2/StoreCore.sol @@ -7,11 +7,13 @@ import { console } from "forge-std/console.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 StoreUpdate(bytes32 table, bytes32[] key, uint8 schemaIndex, bytes data); + event StoreUpdate(bytes32 table, bytes32[] key, uint16 arrayIndex, uint8 fieldIndex, bytes data); + bytes32 constant _slot = keccak256("mud.store"); bytes32 constant _schemaTable = keccak256("mud.store.table.schema"); error StoreCore_SchemaTooLong(); + error StoreCore_NotImplemented(); /************************************************************************ * @@ -29,33 +31,24 @@ library StoreCore { /** * Register a new table schema */ - function registerSchema(bytes32 table, SchemaType[] memory schema) internal { + function registerSchema(bytes32 table, bytes32 schema) internal { // TODO: verify the table doesn't already exist if (schema.length > 32) revert StoreCore_SchemaTooLong(); bytes32[] memory key = new bytes32[](1); key[0] = table; bytes32 location = _getLocation(_schemaTable, key); - _setDataRaw(location, Bytes.from(schema)); + _setDataRaw(location, bytes.concat(schema)); } /** * Get the schema for the given table */ - function getSchema(bytes32 table) internal view returns (SchemaType[] memory schema) { + function getSchema(bytes32 table) internal view returns (bytes32 schema) { bytes32[] memory key = new bytes32[](1); key[0] = table; bytes32 location = _getLocation(_schemaTable, key); bytes memory blob = _getDataRaw(location, 32); - - // Find the first `None` value in the schema to determine the length - uint256 length = 0; - while (length < 32 && blob[length] != bytes1(uint8(SchemaType.None))) { - length++; - } - - // Decrease the blob size to the actual length - Bytes.setLengthInPlace(blob, length); - schema = Bytes.toSchemaTypeArray(blob); + return bytes32(blob); } /************************************************************************ @@ -65,9 +58,10 @@ library StoreCore { ************************************************************************/ /** - * Set full data for the given table and key tuple + * Set full record for the given table and key tuple + * (Note: this will overwrite the entire record, including any array data) */ - function setData( + function set( bytes32 table, bytes32[] memory key, bytes memory data @@ -80,20 +74,35 @@ library StoreCore { _setDataRaw(location, data); // Emit event to notify indexers - emit StoreUpdate(table, key, 0, data); + emit StoreUpdate(table, key, 0, 0, data); } - /** - * Set partial data for the given table and key tuple, at the given schema index - * TODO: implement - */ - function setData( - bytes32 table, - bytes32[] memory key, - uint8 schemaIndex, - bytes memory data - ) internal { - revert("not implemented"); + function setField( + bytes32, + bytes32[] memory, + uint8, + bytes memory + ) internal pure { + revert StoreCore_NotImplemented(); + } + + function setArrayIndex( + bytes32, + bytes32[] memory, + uint16, + bytes memory + ) internal pure { + revert StoreCore_NotImplemented(); + } + + function setArrayIndexField( + bytes32, + bytes32[] memory, + uint16, + uint8, + bytes memory + ) internal pure { + revert StoreCore_NotImplemented(); } /************************************************************************ @@ -103,28 +112,41 @@ library StoreCore { ************************************************************************/ /** - * Get full data for the given table and key tuple (compute length from schema) + * Get full record for the given table and key tuple (compute length from schema) */ - function getData(bytes32 table, bytes32[] memory key) internal view returns (bytes memory) { + function get(bytes32 table, bytes32[] memory key) internal view returns (bytes memory) { // Get schema for this table - SchemaType[] memory schema = getSchema(table); + bytes32 schema = getSchema(table); // Compute length of the full schema - uint256 length = _getByteLength(schema); + uint256 length = _getSchemaLength(schema); return getData(table, key, length); } - /** - * Get partial data for the given table and key tuple, at the given schema index - * TODO: implement - */ - function getPartialData( - bytes32 table, - bytes32[] memory key, - uint8 schemaIndex - ) internal view returns (bytes memory) { - revert("not implemented"); + function getField( + bytes32, + bytes32[] memory, + uint8 + ) internal pure returns (bytes memory) { + revert StoreCore_NotImplemented(); + } + + function getArrayIndex( + bytes32, + bytes32[] memory, + uint16 + ) internal pure returns (bytes memory) { + revert StoreCore_NotImplemented(); + } + + function getArrayIndexField( + bytes32, + bytes32[] memory, + uint16, + uint8 + ) internal pure returns (bytes memory) { + revert StoreCore_NotImplemented(); } /** @@ -146,20 +168,6 @@ library StoreCore { * ************************************************************************/ - /** - * Split the given bytes blob into an array of bytes based on the given schema - */ - function split(bytes memory blob, SchemaType[] memory schema) internal pure returns (bytes[] memory) { - uint256[] memory lengths = new uint256[](schema.length); - for (uint256 i = 0; i < schema.length; ) { - lengths[i] = getByteLength(schema[i]); - unchecked { - i++; - } - } - return Bytes.split(blob, lengths); - } - /************************************************************************ * * INTERNAL HELPER FUNCTIONS @@ -211,15 +219,8 @@ library StoreCore { /** * Get the length of the data for the given schema */ - function _getByteLength(SchemaType[] memory schema) internal pure returns (uint256) { - uint256 length = 0; - for (uint256 i = 0; i < schema.length; ) { - length += getByteLength(schema[i]); - unchecked { - i++; - } - } - return length; + function _getSchemaLength(bytes32 schema) internal pure returns (uint256) { + return uint256(uint16(bytes2(schema))); } } @@ -231,17 +232,17 @@ library StoreCoreExt { * ************************************************************************/ - function setData( + function set( bytes32 table, bytes32 _key, bytes memory data ) internal { bytes32[] memory key = new bytes32[](1); key[0] = _key; - StoreCore.setData(table, key, data); + StoreCore.set(table, key, data); } - function setData( + function set( bytes32 table, bytes32[2] memory _key, bytes memory data @@ -249,7 +250,7 @@ library StoreCoreExt { bytes32[] memory key = new bytes32[](2); key[0] = _key[0]; key[1] = _key[1]; - StoreCore.setData(table, key, data); + StoreCore.set(table, key, data); } /************************************************************************ @@ -257,16 +258,16 @@ library StoreCoreExt { * GET DATA * ************************************************************************/ - function getData(bytes32 table, bytes32 _key) external view returns (bytes memory) { + function get(bytes32 table, bytes32 _key) external view returns (bytes memory) { bytes32[] memory key = new bytes32[](1); key[0] = _key; - return StoreCore.getData(table, key); + return StoreCore.get(table, key); } function getData(bytes32 table, bytes32[2] memory _key) external view returns (bytes memory) { bytes32[] memory key = new bytes32[](2); key[0] = _key[0]; key[1] = _key[1]; - return StoreCore.getData(table, key); + return StoreCore.get(table, key); } } diff --git a/packages/solecs/v2/StoreSwitch.sol b/packages/solecs/v2/StoreSwitch.sol index 814803a23e..e63946fbe0 100644 --- a/packages/solecs/v2/StoreSwitch.sol +++ b/packages/solecs/v2/StoreSwitch.sol @@ -21,7 +21,7 @@ library StoreSwitch { } } - function registerSchema(bytes32 table, SchemaType[] memory schema) internal { + function registerSchema(bytes32 table, bytes32 schema) internal { if (isDelegateCall()) { StoreCore.registerSchema(table, schema); } else { @@ -29,7 +29,7 @@ library StoreSwitch { } } - function getSchema(bytes32 table) internal view returns (SchemaType[] memory schema) { + function getSchema(bytes32 table) internal view returns (bytes32 schema) { if (isDelegateCall()) { schema = StoreCore.getSchema(table); } else { @@ -37,60 +37,100 @@ library StoreSwitch { } } - function setData( + function set( bytes32 table, bytes32[] memory key, bytes memory data ) internal { if (isDelegateCall()) { - StoreCore.setData(table, key, data); + StoreCore.set(table, key, data); } else { - IStore(msg.sender).setData(table, key, data); + IStore(msg.sender).set(table, key, data); } } - function setData( + function setField( bytes32 table, bytes32[] memory key, - uint8 schemaIndex, + uint8 fieldIndex, bytes memory data ) internal { if (isDelegateCall()) { - StoreCore.setData(table, key, schemaIndex, data); + StoreCore.setField(table, key, fieldIndex, data); } else { - IStore(msg.sender).setData(table, key, schemaIndex, data); + IStore(msg.sender).setField(table, key, fieldIndex, data); } } - function getData(bytes32 table, bytes32[] memory key) internal view returns (bytes memory) { + function setArrayIndex( + bytes32 table, + bytes32[] memory key, + uint16 arrayIndex, + bytes memory data + ) internal { + if (isDelegateCall()) { + StoreCore.setArrayIndex(table, key, arrayIndex, data); + } else { + IStore(msg.sender).setArrayIndex(table, key, arrayIndex, data); + } + } + + function setArrayIndexField( + bytes32 table, + bytes32[] memory key, + uint16 arrayIndex, + uint8 fieldIndex, + bytes memory data + ) internal { + if (isDelegateCall()) { + StoreCore.setArrayIndexField(table, key, arrayIndex, fieldIndex, data); + } else { + IStore(msg.sender).setArrayIndexField(table, key, arrayIndex, fieldIndex, data); + } + } + + function get(bytes32 table, bytes32[] memory key) internal view returns (bytes memory) { + if (isDelegateCall()) { + return StoreCore.get(table, key); + } else { + return IStore(msg.sender).get(table, key); + } + } + + function getField( + bytes32 table, + bytes32[] memory key, + uint8 fieldIndex + ) internal view returns (bytes memory) { if (isDelegateCall()) { - return StoreCore.getData(table, key); + return StoreCore.getField(table, key, fieldIndex); } else { - return IStore(msg.sender).getData(table, key); + return IStore(msg.sender).getField(table, key, fieldIndex); } } - function getData( + function getArrayIndex( bytes32 table, bytes32[] memory key, - uint256 length + uint16 arrayIndex ) internal view returns (bytes memory) { if (isDelegateCall()) { - return StoreCore.getData(table, key, length); + return StoreCore.getArrayIndex(table, key, arrayIndex); } else { - return IStore(msg.sender).getData(table, key, length); + return IStore(msg.sender).getArrayIndex(table, key, arrayIndex); } } - function getPartialData( + function getArrayIndexField( bytes32 table, bytes32[] memory key, - uint8 schemaIndex + uint16 arrayIndex, + uint8 fieldIndex ) internal view returns (bytes memory) { if (isDelegateCall()) { - return StoreCore.getPartialData(table, key, schemaIndex); + return StoreCore.getArrayIndexField(table, key, arrayIndex, fieldIndex); } else { - return IStore(msg.sender).getPartialData(table, key, schemaIndex); + return IStore(msg.sender).getArrayIndexField(table, key, arrayIndex, fieldIndex); } } } diff --git a/packages/solecs/v2/StoreView.sol b/packages/solecs/v2/StoreView.sol index 2bffa75f4b..971358c800 100644 --- a/packages/solecs/v2/StoreView.sol +++ b/packages/solecs/v2/StoreView.sol @@ -9,11 +9,15 @@ import { StoreCore } from "./StoreCore.sol"; contract StoreView is IStore { error Store_BaseContractNotImplemented(); - function registerSchema(bytes32, SchemaType[] memory) public virtual { + function getSchema(bytes32 table) public view virtual returns (bytes32 schema) { + schema = StoreCore.getSchema(table); + } + + function registerSchema(bytes32, bytes32) public virtual { revert Store_BaseContractNotImplemented(); } - function setData( + function set( bytes32, bytes32[] memory, bytes memory @@ -21,37 +25,68 @@ contract StoreView is IStore { revert Store_BaseContractNotImplemented(); } - function setData( - bytes32, - bytes32[] memory, - uint8, - bytes memory + // Set partial data at schema index + function setField( + bytes32 table, + bytes32[] memory key, + uint8 schemaIndex, + bytes memory data ) public virtual { revert Store_BaseContractNotImplemented(); } - function getSchema(bytes32 table) public view virtual returns (SchemaType[] memory schema) { - schema = StoreCore.getSchema(table); + // Set full record of a single item at a given array index + function setArrayIndex( + bytes32 table, + bytes32[] memory key, + uint16 arrayIndex, + bytes memory data + ) public virtual { + revert Store_BaseContractNotImplemented(); } - function getData(bytes32 table, bytes32[] memory key) public view virtual returns (bytes memory) { - return StoreCore.getData(table, key); + // Set partial data of a single item at a given array index + function setArrayIndexField( + bytes32 table, + bytes32[] memory key, + uint16 arrayIndex, + uint8 schemaIndex, + bytes memory data + ) public virtual { + revert Store_BaseContractNotImplemented(); } - function getData( + // Get full record (including full array) + function get(bytes32 table, bytes32[] memory key) public view returns (bytes memory data) { + data = StoreCore.get(table, key); + } + + // Get partial data at schema index + function getField( bytes32 table, bytes32[] memory key, - uint256 length - ) public view virtual returns (bytes memory) { - return StoreCore.getData(table, key, length); + uint8 schemaIndex + ) public view returns (bytes memory data) { + revert Store_BaseContractNotImplemented(); } - function getPartialData( + // Get full record of a single item at a given array index + function getArrayIndex( bytes32 table, bytes32[] memory key, + uint16 arrayIndex + ) public view returns (bytes memory data) { + revert Store_BaseContractNotImplemented(); + } + + // Get partial data of a single item at a given array index + function getArrayIndexField( + bytes32 table, + bytes32[] memory key, + uint16 arrayIndex, uint8 schemaIndex - ) public view virtual returns (bytes memory) { - return StoreCore.getPartialData(table, key, schemaIndex); + ) public view returns (bytes memory data) { + revert Store_BaseContractNotImplemented(); } function isStore() public view {} diff --git a/packages/solecs/v2/World.sol b/packages/solecs/v2/World.sol index 2f1cdd14a8..17429d2c50 100644 --- a/packages/solecs/v2/World.sol +++ b/packages/solecs/v2/World.sol @@ -5,7 +5,7 @@ import { console } from "forge-std/console.sol"; import { StoreCore } from "./StoreCore.sol"; import { StoreView } from "./StoreView.sol"; import { SchemaType, ExecutionMode } from "./Types.sol"; -import { RouteTable, Route } from "./tables/RouteTable.sol"; +import { RouteTable, Route, id as RouteId } from "./tables/RouteTable.sol"; import { Bytes } from "./Bytes.sol"; /** @@ -14,25 +14,29 @@ import { Bytes } from "./Bytes.sol"; contract World is StoreView { error World_InvalidSystem(); - function registerSchema(bytes32 table, SchemaType[] memory schema) public override { + constructor() { + registerSchema(RouteId, RouteTable.getSchema()); + } + + function registerSchema(bytes32 table, bytes32 schema) public override { StoreCore.registerSchema(table, schema); } - function setData( + function set( bytes32 table, bytes32[] memory key, bytes memory data ) public override { - StoreCore.setData(table, key, data); + StoreCore.set(table, key, data); } - function setData( + function setField( bytes32 table, bytes32[] memory key, - uint8 schemaIndex, + uint8 fieldIndex, bytes memory data ) public override { - StoreCore.setData(table, key, schemaIndex, data); + StoreCore.setField(table, key, fieldIndex, data); } function registerSystem( diff --git a/packages/solecs/v2/tables/RouteTable.sol b/packages/solecs/v2/tables/RouteTable.sol index 7820f1514f..00b7073b0d 100644 --- a/packages/solecs/v2/tables/RouteTable.sol +++ b/packages/solecs/v2/tables/RouteTable.sol @@ -23,11 +23,16 @@ struct Route { library RouteTable { /** Get the table's schema */ - function getSchema() internal pure returns (SchemaType[] memory schema) { - schema = new SchemaType[](3); - schema[0] = SchemaType.Address; - schema[1] = SchemaType.Bytes4; - schema[2] = SchemaType.Uint8; + function getSchema() internal pure returns (bytes32 schema) { + schema = bytes32( + //
+ bytes.concat( + bytes2(uint16(25)), + bytes1(uint8(SchemaType.Address)), + bytes1(uint8(SchemaType.Bytes4)), + bytes1(uint8(SchemaType.Uint8)) + ) + ); } /** Register the table's schema */ @@ -49,7 +54,7 @@ library RouteTable { bytes memory data = bytes.concat(bytes20(addr), bytes4(selector), bytes1(executionMode)); bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = key; - StoreSwitch.setData(id, keyTuple, data); + StoreSwitch.set(id, keyTuple, data); } function set(bytes32 key, Route memory data) internal { @@ -59,33 +64,33 @@ library RouteTable { function setAddress(bytes32 key, address addr) internal { bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = key; - StoreSwitch.setData(id, keyTuple, 0, bytes.concat(bytes20(addr))); + StoreSwitch.setField(id, keyTuple, 0, bytes.concat(bytes20(addr))); } function setSelector(bytes32 key, bytes4 selector) internal { bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = key; - StoreSwitch.setData(id, keyTuple, 1, bytes.concat(bytes4(selector))); + StoreSwitch.setField(id, keyTuple, 1, bytes.concat(bytes4(selector))); } function setExecutionMode(bytes32 key, uint8 executionMode) internal { bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = key; - StoreSwitch.setData(id, keyTuple, 2, bytes.concat(bytes1(executionMode))); + StoreSwitch.setField(id, keyTuple, 2, bytes.concat(bytes1(executionMode))); } /** Get the table's data */ function get(bytes32 key) internal view returns (Route memory data) { bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = key; - bytes memory blob = StoreSwitch.getData(id, keyTuple, 25); + bytes memory blob = StoreSwitch.get(id, keyTuple); return decode(blob); } function get(IStore store, bytes32 key) internal view returns (Route memory data) { bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = key; - bytes memory blob = store.getData(id, keyTuple, 25); + bytes memory blob = store.get(id, keyTuple); return decode(blob); } diff --git a/packages/solecs/v2/tables/Vector2Table.sol b/packages/solecs/v2/tables/Vector2Table.sol index e3e518949b..b7817fbdbb 100644 --- a/packages/solecs/v2/tables/Vector2Table.sol +++ b/packages/solecs/v2/tables/Vector2Table.sol @@ -22,10 +22,11 @@ struct Vector2 { library Vector2Table { /** Get the table's schema */ - function getSchema() internal pure returns (SchemaType[] memory schema) { - schema = new SchemaType[](2); - schema[0] = SchemaType.Uint32; - schema[1] = SchemaType.Uint32; + function getSchema() internal pure returns (bytes32 schema) { + schema = bytes32( + // + bytes.concat(bytes2(uint16(8)), bytes1(uint8(SchemaType.Uint32)), bytes1(uint8(SchemaType.Uint32))) + ); } /** Register the table's schema */ @@ -46,7 +47,7 @@ library Vector2Table { bytes memory data = bytes.concat(bytes4(x), bytes4(y)); bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = key; - StoreSwitch.setData(id, keyTuple, data); + StoreSwitch.set(id, keyTuple, data); } function set(bytes32 key, Vector2 memory vec2) internal { @@ -56,27 +57,27 @@ library Vector2Table { function setX(bytes32 key, uint32 x) internal { bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = key; - StoreSwitch.setData(id, keyTuple, 0, bytes.concat(bytes4(x))); + StoreSwitch.setField(id, keyTuple, 0, bytes.concat(bytes4(x))); } function setY(bytes32 key, uint32 y) internal { bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = key; - StoreSwitch.setData(id, keyTuple, 1, bytes.concat(bytes4(y))); + StoreSwitch.setField(id, keyTuple, 1, bytes.concat(bytes4(y))); } /** Get the table's data */ function get(bytes32 key) internal view returns (Vector2 memory vec2) { bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = key; - bytes memory blob = StoreSwitch.getData(id, keyTuple, 8); + bytes memory blob = StoreSwitch.get(id, keyTuple); return decode(blob); } function get(IStore store, bytes32 key) internal view returns (Vector2 memory vec2) { bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = key; - bytes memory blob = store.getData(id, keyTuple, 8); + bytes memory blob = store.get(id, keyTuple); return decode(blob); } diff --git a/packages/solecs/v2/test/RouteTable.t.sol b/packages/solecs/v2/test/RouteTable.t.sol index 9533655e2c..678fde4d4a 100644 --- a/packages/solecs/v2/test/RouteTable.t.sol +++ b/packages/solecs/v2/test/RouteTable.t.sol @@ -15,8 +15,8 @@ contract SystemTableTest is DSTestPlus, StoreView { gas = gas - gasleft(); console.log("gas used: %s", gas); - SchemaType[] memory registeredSchema = StoreCore.getSchema(RouteTableId); - SchemaType[] memory declaredSchema = RouteTable.getSchema(); + bytes32 registeredSchema = StoreCore.getSchema(RouteTableId); + bytes32 declaredSchema = RouteTable.getSchema(); assertEq(keccak256(abi.encode(registeredSchema)), keccak256(abi.encode(declaredSchema))); } diff --git a/packages/solecs/v2/test/StoreCore.t.sol b/packages/solecs/v2/test/StoreCore.t.sol index 98fc6c5df6..8db8a54652 100644 --- a/packages/solecs/v2/test/StoreCore.t.sol +++ b/packages/solecs/v2/test/StoreCore.t.sol @@ -9,6 +9,19 @@ import { Bytes } from "../Bytes.sol"; import { SchemaType } from "../Types.sol"; contract StoreCoreTest is DSTestPlus { + function testGetSchemaLength() public { + bytes32 schema = bytes32( + bytes.concat(bytes2(uint16(3)), bytes1(uint8(SchemaType.Uint8)), bytes1(uint8(SchemaType.Uint16))) + ); + + uint256 gas = gasleft(); + uint256 length = StoreCore._getSchemaLength(schema); + gas = gas - gasleft(); + console.log("gas used (set): %s", gas); + + assertEq(length, 3); + } + function testSetAndGetDataRawOneSlot() public { bytes32 location = keccak256("some location"); bytes memory data = new bytes(32); @@ -47,11 +60,15 @@ contract StoreCoreTest is DSTestPlus { } function testRegisterAndGetSchema() public { - SchemaType[] memory schema = new SchemaType[](4); - schema[0] = SchemaType.Uint8; - schema[1] = SchemaType.Uint16; - schema[2] = SchemaType.Uint8; - schema[3] = SchemaType.Uint16; + bytes32 schema = bytes32( + bytes.concat( + bytes2(uint16(6)), + bytes1(uint8(SchemaType.Uint8)), + bytes1(uint8(SchemaType.Uint16)), + bytes1(uint8(SchemaType.Uint8)), + bytes1(uint8(SchemaType.Uint16)) + ) + ); bytes32 table = keccak256("some.table"); uint256 gas = gasleft(); @@ -60,45 +77,29 @@ contract StoreCoreTest is DSTestPlus { console.log("gas used (register): %s", gas); gas = gasleft(); - SchemaType[] memory loadedSchema = StoreCore.getSchema(table); + bytes memory loadedSchema = bytes.concat(StoreCore.getSchema(table)); gas = gas - gasleft(); console.log("gas used (get schema, warm): %s", gas); assertEq(loadedSchema.length, schema.length); - assertEq(uint8(schema[0]), uint8(loadedSchema[0])); - assertEq(uint8(schema[1]), uint8(loadedSchema[1])); - assertEq(uint8(schema[2]), uint8(loadedSchema[2])); - assertEq(uint8(schema[3]), uint8(loadedSchema[3])); + assertEq(uint8(Bytes.slice1(loadedSchema, 2)), uint8(SchemaType.Uint8)); + assertEq(uint8(Bytes.slice1(loadedSchema, 3)), uint8(SchemaType.Uint16)); + assertEq(uint8(Bytes.slice1(loadedSchema, 4)), uint8(SchemaType.Uint8)); + assertEq(uint8(Bytes.slice1(loadedSchema, 5)), uint8(SchemaType.Uint16)); } - function testSplit() public { - SchemaType[] memory schema = new SchemaType[](4); - schema[0] = SchemaType.Uint8; - schema[1] = SchemaType.Uint16; - schema[2] = SchemaType.Uint16; - schema[3] = SchemaType.Uint32; - - bytes memory data = bytes.concat(bytes1(0x01), bytes2(0x0203), bytes2(0x0405), bytes4(0x06070809)); - - uint256 gas = gasleft(); - bytes[] memory splitData = StoreCore.split(data, schema); - gas = gas - gasleft(); - console.log("gas used: %s", gas); - - assertEq(splitData.length, schema.length); - assertEq(uint8(bytes1(splitData[0])), 0x01); - assertEq(uint16(bytes2(splitData[1])), 0x0203); - assertEq(uint16(bytes2(splitData[2])), 0x0405); - assertEq(uint32(bytes4(splitData[3])), 0x06070809); - } - - function testSetAndGetAndSplitData() public { + function testSetAndGetData() public { // Register table's schema - SchemaType[] memory schema = new SchemaType[](4); - schema[0] = SchemaType.Uint8; - schema[1] = SchemaType.Uint16; - schema[2] = SchemaType.Uint8; - schema[3] = SchemaType.Uint16; + bytes32 schema = bytes32( + bytes.concat( + bytes2(uint16(6)), + bytes1(uint8(SchemaType.Uint8)), + bytes1(uint8(SchemaType.Uint16)), + bytes1(uint8(SchemaType.Uint8)), + bytes1(uint8(SchemaType.Uint16)) + ) + ); + bytes32 table = keccak256("some.table"); StoreCore.registerSchema(table, schema); @@ -109,12 +110,12 @@ contract StoreCoreTest is DSTestPlus { key[0] = keccak256("some.key"); uint256 gas = gasleft(); - uint256 length = StoreCore._getByteLength(schema); + uint256 length = StoreCore._getSchemaLength(schema); gas = gas - gasleft(); console.log("gas used (compute schema length): %s", gas); gas = gasleft(); - StoreCore.setData(table, key, data); + StoreCore.set(table, key, data); gas = gas - gasleft(); console.log("gas used (set): %s", gas); @@ -124,16 +125,6 @@ contract StoreCoreTest is DSTestPlus { gas = gas - gasleft(); console.log("gas used (get, warm): %s", gas); - // Split data - gas = gasleft(); - bytes[] memory splitData = StoreCore.split(data, schema); - gas = gas - gasleft(); - console.log("gas used (split): %s", gas); - - assertEq(loadedData.length, data.length); - assertEq(uint8(bytes1(splitData[0])), 0x01); - assertEq(uint16(bytes2(splitData[1])), 0x0203); - assertEq(uint8(bytes1(splitData[2])), 0x04); - assertEq(uint16(bytes2(splitData[3])), 0x0506); + assertTrue(Bytes.equals(data, loadedData)); } } diff --git a/packages/solecs/v2/test/Vector2Table.t.sol b/packages/solecs/v2/test/Vector2Table.t.sol index 02ff239b2e..29a84f0e86 100644 --- a/packages/solecs/v2/test/Vector2Table.t.sol +++ b/packages/solecs/v2/test/Vector2Table.t.sol @@ -15,8 +15,8 @@ contract Vector2TableTest is DSTestPlus, StoreView { gas = gas - gasleft(); console.log("gas used: %s", gas); - SchemaType[] memory registeredSchema = StoreCore.getSchema(Vector2Id); - SchemaType[] memory declaredSchema = Vector2Table.getSchema(); + bytes32 registeredSchema = StoreCore.getSchema(Vector2Id); + bytes32 declaredSchema = Vector2Table.getSchema(); assertEq(keccak256(abi.encode(registeredSchema)), keccak256(abi.encode(declaredSchema))); } diff --git a/packages/solecs/v2/test/World.t.sol b/packages/solecs/v2/test/World.t.sol index d079d67e4d..dc9fa1f249 100644 --- a/packages/solecs/v2/test/World.t.sol +++ b/packages/solecs/v2/test/World.t.sol @@ -44,8 +44,6 @@ contract WorldTest is DSTestPlus { system = new TestSystem(); } - function _registerSystem(ExecutionMode executionMode) internal {} - function testRegisterAndCallSystem() public { // Register autonomous system in the world world.registerSystem(address(system), "TestSystem", "msgSender()", ExecutionMode.Autonomous); From b4ed72216050537aaf6d05b678dab453f3a2545a Mon Sep 17 00:00:00 2001 From: alvrs Date: Wed, 25 Jan 2023 11:49:29 +0100 Subject: [PATCH 12/82] feat(solecs): wip data model - add logic to set partial word --- packages/solecs/v2/Bytes.sol | 8 ++ packages/solecs/v2/StoreCore.sol | 154 +++++++++++++++++++----- packages/solecs/v2/Types.sol | 6 + packages/solecs/v2/test/StoreCore.t.sol | 133 ++++++++++++++++++-- 4 files changed, 261 insertions(+), 40 deletions(-) diff --git a/packages/solecs/v2/Bytes.sol b/packages/solecs/v2/Bytes.sol index eea86577fa..a4b86fb063 100644 --- a/packages/solecs/v2/Bytes.sol +++ b/packages/solecs/v2/Bytes.sol @@ -268,6 +268,14 @@ library Bytes { return output; } + function slice1(bytes32 data, uint256 start) internal pure returns (bytes1) { + bytes1 output; + assembly { + output := shl(mul(8, start), data) + } + return output; + } + /** Slice bytes to bytes2 without copying data */ function slice2(bytes memory data, uint256 start) internal pure returns (bytes2) { bytes2 output; diff --git a/packages/solecs/v2/StoreCore.sol b/packages/solecs/v2/StoreCore.sol index 11d9305ede..c83781268c 100644 --- a/packages/solecs/v2/StoreCore.sol +++ b/packages/solecs/v2/StoreCore.sol @@ -14,6 +14,7 @@ library StoreCore { error StoreCore_SchemaTooLong(); error StoreCore_NotImplemented(); + error StoreCore_InvalidDataLength(uint256 expected, uint256 received); /************************************************************************ * @@ -33,11 +34,10 @@ library StoreCore { */ function registerSchema(bytes32 table, bytes32 schema) internal { // TODO: verify the table doesn't already exist - if (schema.length > 32) revert StoreCore_SchemaTooLong(); bytes32[] memory key = new bytes32[](1); key[0] = table; bytes32 location = _getLocation(_schemaTable, key); - _setDataRaw(location, bytes.concat(schema)); + _setDataUnchecked(location, 0, bytes.concat(schema)); } /** @@ -47,7 +47,7 @@ library StoreCore { bytes32[] memory key = new bytes32[](1); key[0] = table; bytes32 location = _getLocation(_schemaTable, key); - bytes memory blob = _getDataRaw(location, 32); + bytes memory blob = _getDataUnchecked(location, 32); return bytes32(blob); } @@ -66,24 +66,39 @@ library StoreCore { bytes32[] memory key, bytes memory data ) internal { - // TODO: verify the value has the correct length for the table (based on the table's schema) - // (Tradeoff, slightly higher cost due to additional sload, but higher security - library could also provide both options) + // verify the value has the correct length for the table (based on the table's schema) + // to prevent invalid data from being stored + bytes32 schema = getSchema(table); + if (_getSchemaLength(schema) != data.length) + revert StoreCore_InvalidDataLength(_getSchemaLength(schema), data.length); // Store the provided value in storage bytes32 location = _getLocation(table, key); - _setDataRaw(location, data); + _setDataUnchecked(location, 0, data); // Emit event to notify indexers emit StoreUpdate(table, key, 0, 0, data); } function setField( - bytes32, - bytes32[] memory, - uint8, - bytes memory - ) internal pure { - revert StoreCore_NotImplemented(); + bytes32 table, + bytes32[] memory key, + uint8 fieldIndex, + bytes memory data + ) internal { + // verify the value has the correct length for the field + bytes32 schema = getSchema(table); + SchemaType schemaType = _getSchemaTypeAtIndex(schema, fieldIndex); + if (getByteLength(schemaType) != data.length) + revert StoreCore_InvalidDataLength(getByteLength(schemaType), data.length); + + // Store the provided value in storage + bytes32 location = _getLocation(table, key); + uint256 offset = _getDataOffset(schema, fieldIndex); + _setDataUnchecked(location, offset, data); + + // Emit event to notify indexers + emit StoreUpdate(table, key, 0, 0, data); } function setArrayIndex( @@ -118,10 +133,7 @@ library StoreCore { // Get schema for this table bytes32 schema = getSchema(table); - // Compute length of the full schema - uint256 length = _getSchemaLength(schema); - - return getData(table, key, length); + return get(table, key, schema); } function getField( @@ -152,14 +164,14 @@ library StoreCore { /** * Get full data for the given table and key tuple, with the given length */ - function getData( + function get( bytes32 table, bytes32[] memory key, - uint256 length + bytes32 schema ) internal view returns (bytes memory) { // Load the data from storage bytes32 location = _getLocation(table, key); - return _getDataRaw(location, length); + return _getDataUnchecked(location, _getSchemaLength(schema)); } /************************************************************************ @@ -182,27 +194,73 @@ library StoreCore { return keccak256(abi.encode(_slot, table, key)); } + function _setFullWord(bytes32 location, bytes32 data) internal { + assembly { + sstore(location, data) + } + } + + function _setPartialWord( + bytes32 location, + uint256 offset, + uint256 length, + bytes32 data + ) internal { + bytes32 current; + assembly { + current := sload(location) + } + bytes32 mask = bytes32(((1 << (length * 8)) - 1) << (256 - length * 8 - offset * 8)); // create a mask for the bits we want to update + console.logBytes32(mask); + bytes32 updated = (current & ~mask) | ((data >> (offset * 8)) & mask); // apply mask to data + console.logBytes32(updated); + assembly { + sstore(location, updated) + } + } + /** * Write raw bytes to storage at the given location + * TODO: this implementation is optimized for readability, but not very gas efficient. We should optimize this using assembly once we've settled on a spec. */ - function _setDataRaw(bytes32 location, bytes memory data) internal { - assembly { - // loop over data and sstore it, starting at `location` - // (don't store length, since it is known from the schema) - for { - let i := 0 - } lt(i, mload(data)) { - i := add(i, 0x20) // increment by 32 since we are storing 32 bytes at a time - } { - sstore(add(location, i), mload(add(data, add(0x20, i)))) + function _setDataUnchecked( + bytes32 location, + uint256 offset, + bytes memory data + ) internal { + uint256 numWords = Utils.divCeil(data.length, 32); + + for (uint256 i; i < numWords; i++) { + // If this is the first word, and there is an offset, apply a mask to beginning + if ((i == 0 && offset > 0)) { + _setPartialWord( + location, // the word to update + offset, // the offset in bytes to start writing + data.length > 32 ? 32 - offset : data.length, // the number of bytes to write + bytes32(data) + ); + + // If this is the last word, and there is a partial word, apply a mask to the end + } else if (i == numWords - 1 && data.length % 32 > 0) { + _setPartialWord( + bytes32(uint256(location) + i * 32), // the word to update + 0, // the offset in bytes to start writing + data.length % 32, // the number of bytes to write + Bytes.slice32(data, i * 32) // the data to write + ); + + // Otherwise, just write the word + } else { + _setFullWord(bytes32(uint256(location) + i * 32), Bytes.slice32(data, i * 32)); } } } /** * Read raw bytes from storage at the given location and length in bytess + * TODO: implement offset */ - function _getDataRaw(bytes32 location, uint256 length) internal view returns (bytes memory data) { + function _getDataUnchecked(bytes32 location, uint256 length) internal view returns (bytes memory data) { data = new bytes(length); // load data from storage into memory assembly { @@ -222,6 +280,42 @@ library StoreCore { function _getSchemaLength(bytes32 schema) internal pure returns (uint256) { return uint256(uint16(bytes2(schema))); } + + /** + * Get the offset of the data for the given schema at the given index + * TODO: gas optimize + */ + function _getDataOffset(bytes32 schema, uint8 fieldIndex) internal pure returns (uint256) { + uint256 offset = 2; // skip length + for (uint256 i = 0; i < fieldIndex; i++) { + offset += getByteLength(_getSchemaTypeAtIndex(schema, i)); + } + return offset; + } + + /** + * Get the type of the data for the given schema at the given index + */ + function _getSchemaTypeAtIndex(bytes32 schema, uint256 index) internal pure returns (SchemaType) { + return SchemaType(uint8(Bytes.slice1(schema, index + 2))); + } + + /** + * Encode the given schema into a single bytes32 + * TODO: gas optimize, replace bytes.concat with Buffer + */ + function _encodeSchema(SchemaType[] memory _schema) internal pure returns (bytes32) { + if (_schema.length > 30) revert StoreCore_SchemaTooLong(); + uint16 length; + bytes memory schema; + + for (uint256 i = 0; i < _schema.length; i++) { + length += uint16(getByteLength(_schema[i])); + schema = bytes.concat(schema, bytes1(uint8(_schema[i]))); + } + + return bytes32(bytes.concat(bytes2(length), schema)); + } } // Overloads for single key and some fixed length array keys for better devex diff --git a/packages/solecs/v2/Types.sol b/packages/solecs/v2/Types.sol index 23263424da..20651e9814 100644 --- a/packages/solecs/v2/Types.sol +++ b/packages/solecs/v2/Types.sol @@ -7,6 +7,8 @@ enum SchemaType { Uint8, Uint16, Uint32, + Uint128, + Uint256, Bytes4, Address } @@ -23,6 +25,10 @@ function getByteLength(SchemaType schemaType) pure returns (uint256) { return 2; } else if (schemaType == SchemaType.Uint32 || schemaType == SchemaType.Bytes4) { return 4; + } else if (schemaType == SchemaType.Uint128) { + return 16; + } else if (schemaType == SchemaType.Uint256) { + return 32; } else if (schemaType == SchemaType.Address) { return 20; } else { diff --git a/packages/solecs/v2/test/StoreCore.t.sol b/packages/solecs/v2/test/StoreCore.t.sol index 8db8a54652..084e65277c 100644 --- a/packages/solecs/v2/test/StoreCore.t.sol +++ b/packages/solecs/v2/test/StoreCore.t.sol @@ -17,12 +17,43 @@ contract StoreCoreTest is DSTestPlus { uint256 gas = gasleft(); uint256 length = StoreCore._getSchemaLength(schema); gas = gas - gasleft(); - console.log("gas used (set): %s", gas); + console.log("gas used: %s", gas); assertEq(length, 3); } - function testSetAndGetDataRawOneSlot() public { + function testEncodeDecodeSchema() public { + SchemaType[] memory schema = new SchemaType[](5); + schema[0] = SchemaType.Uint8; // 1 byte + schema[1] = SchemaType.Uint16; // 2 bytes + schema[2] = SchemaType.Uint32; // 4 bytes + schema[3] = SchemaType.Uint128; // 4 bytes + schema[4] = SchemaType.Uint256; // 4 bytes + + uint256 gas = gasleft(); + bytes32 encodedSchema = StoreCore._encodeSchema(schema); + gas = gas - gasleft(); + console.log("gas used (encode): %s", gas); + + gas = gasleft(); + uint256 length = StoreCore._getSchemaLength(encodedSchema); + gas = gas - gasleft(); + console.log("gas used (get length): %s", gas); + + gas = gasleft(); + SchemaType schemaType1 = StoreCore._getSchemaTypeAtIndex(encodedSchema, 0); + gas = gas - gasleft(); + console.log("gas used (decode): %s", gas); + + assertEq(length, 55); + assertEq(uint8(schemaType1), uint8(SchemaType.Uint8)); + assertEq(uint8(StoreCore._getSchemaTypeAtIndex(encodedSchema, 1)), uint8(SchemaType.Uint16)); + assertEq(uint8(StoreCore._getSchemaTypeAtIndex(encodedSchema, 2)), uint8(SchemaType.Uint32)); + assertEq(uint8(StoreCore._getSchemaTypeAtIndex(encodedSchema, 3)), uint8(SchemaType.Uint128)); + assertEq(uint8(StoreCore._getSchemaTypeAtIndex(encodedSchema, 4)), uint8(SchemaType.Uint256)); + } + + function testSetAndGetDataUncheckedOneSlot() public { bytes32 location = keccak256("some location"); bytes memory data = new bytes(32); @@ -30,35 +61,55 @@ contract StoreCoreTest is DSTestPlus { data[31] = 0x02; uint256 gas = gasleft(); - StoreCore._setDataRaw(location, data); + StoreCore._setDataUnchecked(location, 0, data); gas = gas - gasleft(); console.log("gas used (set): %s", gas); gas = gasleft(); - bytes memory loadedData = StoreCore._getDataRaw(location, data.length); + bytes memory loadedData = StoreCore._getDataUnchecked(location, data.length); gas = gas - gasleft(); console.log("gas used (get, warm): %s", gas); assertEq(bytes32(loadedData), bytes32(data)); } - function testSetAndGetDataRawMultipleSlots() public { + function testSetAndGetDataUncheckedMultipleSlots() public { bytes32 location = keccak256("some location"); bytes memory data = abi.encode("this is some data spanning multiple words"); uint256 gas = gasleft(); - StoreCore._setDataRaw(location, data); + StoreCore._setDataUnchecked(location, 0, data); gas = gas - gasleft(); console.log("gas used (set, %s slots): %s", Utils.divCeil(data.length, 32), gas); gas = gasleft(); - bytes memory loadedData = StoreCore._getDataRaw(location, data.length); + bytes memory loadedData = StoreCore._getDataUnchecked(location, data.length); gas = gas - gasleft(); console.log("gas used (get, warm, %s slots): %s", Utils.divCeil(data.length, 32), gas); assertTrue(Bytes.equals(data, loadedData)); } + function testSetAndGetDataUncheckedOffset() public { + bytes32 location = keccak256("some location"); + bytes memory data = new bytes(16); + + data[0] = 0x01; + data[15] = 0x02; + + uint256 gas = gasleft(); + StoreCore._setDataUnchecked(location, 8, data); + gas = gas - gasleft(); + console.log("gas used (set): %s", gas); + + gas = gasleft(); + bytes memory loadedData = StoreCore._getDataUnchecked(location, data.length); + gas = gas - gasleft(); + console.log("gas used (get, warm): %s", gas); + + assertEq(bytes32(loadedData), bytes32(bytes.concat(bytes8(0), data))); + } + function testRegisterAndGetSchema() public { bytes32 schema = bytes32( bytes.concat( @@ -110,21 +161,83 @@ contract StoreCoreTest is DSTestPlus { key[0] = keccak256("some.key"); uint256 gas = gasleft(); - uint256 length = StoreCore._getSchemaLength(schema); + StoreCore.set(table, key, data); gas = gas - gasleft(); - console.log("gas used (compute schema length): %s", gas); + console.log("gas used (set): %s", gas); + // Get data gas = gasleft(); + bytes memory loadedData = StoreCore.get(table, key, schema); + gas = gas - gasleft(); + console.log("gas used (get, warm): %s", gas); + + assertTrue(Bytes.equals(data, loadedData)); + } + + function testFailSetAndGetData() public { + // Register table's schema + bytes32 schema = bytes32( + bytes.concat( + bytes2(uint16(6)), + bytes1(uint8(SchemaType.Uint8)), + bytes1(uint8(SchemaType.Uint16)), + bytes1(uint8(SchemaType.Uint8)), + bytes1(uint8(SchemaType.Uint16)) + ) + ); + + bytes32 table = keccak256("some.table"); + StoreCore.registerSchema(table, schema); + + // Set data + bytes memory data = bytes.concat(bytes1(0x01), bytes2(0x0203), bytes1(0x04)); + + bytes32[] memory key = new bytes32[](1); + key[0] = keccak256("some.key"); + + uint256 gas = gasleft(); + StoreCore.set(table, key, data); + gas = gas - gasleft(); + console.log("gas used (set): %s", gas); + } + + function testSetAndGetDataSpanningWords() public { + // Register table's schema + SchemaType[] memory schemaTypes = new SchemaType[](2); + schemaTypes[0] = SchemaType.Uint128; + schemaTypes[1] = SchemaType.Uint256; + + bytes32 schema = StoreCore._encodeSchema(schemaTypes); + + bytes32 table = keccak256("some.table"); + StoreCore.registerSchema(table, schema); + + // Set data + bytes memory data = bytes.concat( + bytes16(0x0102030405060708090a0b0c0d0e0f10), + bytes32(0x1112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f30) + ); + + bytes32[] memory key = new bytes32[](1); + key[0] = keccak256("some.key"); + + uint256 gas = gasleft(); StoreCore.set(table, key, data); gas = gas - gasleft(); console.log("gas used (set): %s", gas); // Get data gas = gasleft(); - bytes memory loadedData = StoreCore.getData(table, key, length); + bytes memory loadedData = StoreCore.get(table, key, schema); gas = gas - gasleft(); console.log("gas used (get, warm): %s", gas); assertTrue(Bytes.equals(data, loadedData)); } + + function testSetFieldWithOffset() public { + // Should not override the data before the offset + // Should not override the data after the field + // revert("todo"); + } } From 0d8ea21aeed6ee02918d0d63795e0729b2d0cb26 Mon Sep 17 00:00:00 2001 From: alvrs Date: Wed, 25 Jan 2023 15:54:55 +0100 Subject: [PATCH 13/82] feat(solecs): wip data model - add low level buffer library --- packages/solecs/v2/Buffer.sol | 279 +++++++++++++++++++++++++++ packages/solecs/v2/Cast.sol | 16 ++ packages/solecs/v2/StoreCore.sol | 8 +- packages/solecs/v2/test/Buffer.t.sol | 189 ++++++++++++++++++ 4 files changed, 490 insertions(+), 2 deletions(-) create mode 100644 packages/solecs/v2/Buffer.sol create mode 100644 packages/solecs/v2/Cast.sol create mode 100644 packages/solecs/v2/test/Buffer.t.sol diff --git a/packages/solecs/v2/Buffer.sol b/packages/solecs/v2/Buffer.sol new file mode 100644 index 0000000000..14439d9649 --- /dev/null +++ b/packages/solecs/v2/Buffer.sol @@ -0,0 +1,279 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +// Acknowledgements: +// Based on @dk1a's Slice.sol library (https://github.com/dk1a/solidity-stringutils/blob/main/src/Slice.sol) +// Inspired by @ethier's DynamicBuffer.sol library (https://github.com/divergencetech/ethier/blob/main/contracts/utils/DynamicBuffer.sol) + +// Optimized for allocating a fixed size buffer and writing to it / reading from it. + +// First 16 bytes are the pointer to the data, followed by 16 bytes of data length. +type Buffer is uint256; + +using BufferUtils for Buffer global; + +// Static functions +library Buffer_ { + /** + * @dev Allocates a new buffer with the given capacity. + */ + function allocate(uint128 capacity) internal pure returns (Buffer) { + uint256 ptr; + assembly { + let buf := mload(0x40) // free memory pointer + mstore(0x40, add(buf, add(32, capacity))) // 32 bytes for the buffer header, plus the length of the buffer + ptr := add(buf, 32) // ptr to first data byte + } + + // Pointer is stored in upper 128 bits, capacity is stored in lower 128 bits + return Buffer.wrap(((ptr << 128) | capacity)); + } + + /** + * @dev Converts a bytes array to a buffer (without copying data) + */ + function fromBytes(bytes memory data) internal pure returns (Buffer) { + uint256 ptr; + assembly { + ptr := add(data, 32) // ptr to first data byte + } + + // Pointer is stored in upper 128 bits, length is stored in lower 128 bits + return Buffer.wrap((ptr << 128) | uint128(data.length)); + } +} + +library BufferUtils { + uint256 constant MASK_CAPACITY = uint256(type(uint128).max); + uint256 constant MASK_PTR = uint256(type(uint128).max) << 128; + + error Buffer_Overflow(); + + /** + * @dev Returns the pointer to the start of an in-memory buffer. + */ + function ptr(Buffer self) internal pure returns (uint256) { + return Buffer.unwrap(self) >> 128; + } + + /** + * @dev Returns the current length in bytes. + */ + function length(Buffer self) internal pure returns (uint128 _length) { + uint256 _ptr = ptr(self); + assembly { + _length := mload(sub(_ptr, 32)) + } + } + + /** + * @dev Returns the capacity in bytes. + */ + function capacity(Buffer self) internal pure returns (uint128 _capacity) { + return uint128(Buffer.unwrap(self)); + } + + /** + * @dev Read a 32 bytes from the buffer starting at the given offset (without checking for overflows) + */ + function read32(Buffer self, uint256 offset) internal pure returns (bytes32 value) { + uint256 _ptr = ptr(self); + assembly { + value := mload(add(_ptr, offset)) + } + } + + /** + * @dev Read a 8 bytes from the buffer starting at the given offset (without checking for overflows) + */ + function read8(Buffer self, uint256 offset) internal pure returns (bytes8 value) { + return bytes8(read32(self, offset)); + } + + /** + * @dev Read a 1 byte from the buffer starting at the given offset + */ + function read1(Buffer self, uint256 offset) internal pure returns (bytes1 value) { + return bytes1(read32(self, offset)); + } + + // TODO: add more typed utils to read from the buffer + + /** + * @dev Appends the given bytes to the buffer (checking for overflows) + */ + function append(Buffer self, bytes memory data) internal pure { + uint128 _newLength = length(self) + uint128(data.length); + _checkCapacity(self, _newLength); + _appendUnchecked(self, data); + } + + /** + * @dev Appends the given bytes to the buffer (without checking for overflows) + */ + function _appendUnchecked(Buffer self, bytes memory data) internal pure { + uint256 _ptr = ptr(self); + uint128 _dataLength = uint128(data.length); + uint128 _length = length(self); + uint128 _newLength = _length + uint128(data.length); + + // Update the current buffer length + _setLengthUnchecked(self, _newLength); + + // Copy over given data to the buffer + assembly { + for { + let i := 0 + } lt(i, _dataLength) { + i := add(i, 32) + } { + mstore(add(add(_ptr, _length), i), mload(add(data, add(32, i)))) + } + } + } + + /** + * @dev returns the buffer as a bytes array (without copying the data) + */ + function toBytes(Buffer self) internal pure returns (bytes memory _bytes) { + uint256 _ptr = ptr(self); + assembly { + _bytes := sub(_ptr, 32) + } + } + + function slice( + Buffer self, + uint256 _start, + uint256 _length + ) internal pure returns (bytes memory) { + return sliceBytes(toBytes(self), _start, _length); + } + + /** + * @dev copies a slice of the buffer to a new memory location and returns it as a bytes array (without checking for overflows) + */ + + /** + * @dev copies the buffer contents to a new memory location and lays it out like a memory array with the given size per element (in bytes) + * @return arrayPtr the pointer to the start of the array, needs to be casted to the expected type using assembly + */ + function toArray(Buffer self, uint256 elementSize) internal pure returns (uint256 arrayPtr) { + uint256 _ptr = ptr(self); + uint128 _length = length(self); + uint256 elementShift = 256 - elementSize * 8; + + assembly { + arrayPtr := mload(0x40) // free memory pointer + let arrayLen := div(_length, elementSize) // array length + mstore(0x40, add(arrayPtr, add(mul(arrayLen, 32), 32))) // update free memory pointer (array length + array data) + mstore(arrayPtr, arrayLen) // store array length + + for { + let i := 0 + let arrayCursor := add(arrayPtr, 32) // skip array length + let dataCursor := _ptr + } lt(i, arrayLen) { + // loop until we reach the end of the buffer + i := add(i, 1) + dataCursor := add(dataCursor, elementSize) // increment buffer pointer by one element size + arrayCursor := add(arrayCursor, 32) // increment array pointer by one slot + } { + mstore(arrayCursor, shr(elementShift, mload(dataCursor))) // copy one element from buffer to array + } + } + } + + /** + * @dev Modify buffer length (checking capacity) + */ + function _setLength(Buffer self, uint128 _length) internal pure { + _checkCapacity(self, _length); + _setLengthUnchecked(self, _length); + } + + /** + * @dev Modify buffer length (without checking capacity) + */ + function _setLengthUnchecked(Buffer self, uint128 _length) internal pure { + uint256 _ptr = ptr(self); + assembly { + mstore(sub(_ptr, 32), _length) + } + } + + /** + * @dev Check if the buffer has enough capacity to store a given length. + */ + function _checkCapacity(Buffer self, uint128 _length) internal pure { + if (capacity(self) < _length) { + revert Buffer_Overflow(); + } + } +} + +// From https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol +function sliceBytes( + bytes memory _bytes, + uint256 _start, + uint256 _length +) pure returns (bytes memory) { + require(_length + 31 >= _length, "slice_overflow"); + require(_bytes.length >= _start + _length, "slice_outOfBounds"); + + bytes memory tempBytes; + + assembly { + switch iszero(_length) + case 0 { + // Get a location of some free memory and store it in tempBytes as + // Solidity does for memory variables. + tempBytes := mload(0x40) + + // The first word of the slice result is potentially a partial + // word read from the original array. To read it, we calculate + // the length of that partial word and start copying that many + // bytes into the array. The first word we copy will start with + // data we don't care about, but the last `lengthmod` bytes will + // land at the beginning of the contents of the new array. When + // we're done copying, we overwrite the full first word with + // the actual length of the slice. + let lengthmod := and(_length, 31) + + // The multiplication in the next line is necessary + // because when slicing multiples of 32 bytes (lengthmod == 0) + // the following copy loop was copying the origin's length + // and then ending prematurely not copying everything it should. + let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod))) + let end := add(mc, _length) + + for { + // The multiplication in the next line has the same exact purpose + // as the one above. + let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start) + } lt(mc, end) { + mc := add(mc, 0x20) + cc := add(cc, 0x20) + } { + mstore(mc, mload(cc)) + } + + mstore(tempBytes, _length) + + //update free-memory pointer + //allocating the array padded to 32 bytes like the compiler does now + mstore(0x40, and(add(mc, 31), not(31))) + } + //if we want a zero-length slice let's just return a zero-length array + default { + tempBytes := mload(0x40) + //zero out the 32 bytes slice we are about to return + //we need to do it because Solidity does not garbage collect + mstore(tempBytes, 0) + + mstore(0x40, add(tempBytes, 0x20)) + } + } + + return tempBytes; +} diff --git a/packages/solecs/v2/Cast.sol b/packages/solecs/v2/Cast.sol new file mode 100644 index 0000000000..f57ba1d914 --- /dev/null +++ b/packages/solecs/v2/Cast.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +library Cast { + function toUint32Array(uint256 ptr) internal pure returns (uint32[] memory arr) { + assembly { + arr := ptr + } + } + + function toUint256Array(uint256 ptr) internal pure returns (uint256[] memory arr) { + assembly { + arr := ptr + } + } +} diff --git a/packages/solecs/v2/StoreCore.sol b/packages/solecs/v2/StoreCore.sol index c83781268c..987b9a4966 100644 --- a/packages/solecs/v2/StoreCore.sol +++ b/packages/solecs/v2/StoreCore.sol @@ -202,8 +202,8 @@ library StoreCore { function _setPartialWord( bytes32 location, - uint256 offset, - uint256 length, + uint256 offset, // in bytes + uint256 length, // in bytes bytes32 data ) internal { bytes32 current; @@ -272,6 +272,10 @@ library StoreCore { mstore(add(data, add(0x20, i)), sload(add(location, i))) } } + + // remove offset + + // remove unused trailing data } /** diff --git a/packages/solecs/v2/test/Buffer.t.sol b/packages/solecs/v2/test/Buffer.t.sol new file mode 100644 index 0000000000..babfc5f52a --- /dev/null +++ b/packages/solecs/v2/test/Buffer.t.sol @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import { console } from "forge-std/console.sol"; +import { DSTestPlus } from "solmate/test/utils/DSTestPlus.sol"; +import { Cast } from "../Cast.sol"; +import "../Buffer.sol"; + +contract BufferTest is DSTestPlus { + function testAllocateBuffer() public { + uint256 gas = gasleft(); + Buffer buf = Buffer_.allocate(32); + gas = gas - gasleft(); + console.log("gas used (Buffer_.allocate): %s", gas); + + gas = gasleft(); + buf.length(); + gas = gas - gasleft(); + console.log("gas used (length): %s", gas); + + gas = gasleft(); + buf.ptr(); + gas = gas - gasleft(); + console.log("gas used (ptr): %s", gas); + + gas = gasleft(); + buf.capacity(); + gas = gas - gasleft(); + console.log("gas used (capacity): %s", gas); + + assertEq(uint256(buf.length()), 0); + assertEq(uint256(buf.capacity()), 32); + } + + function testFromBytes() public { + bytes memory data = bytes.concat(bytes8(0x0102030405060708)); + uint256 gas = gasleft(); + Buffer buf = Buffer_.fromBytes(data); + gas = gas - gasleft(); + console.log("gas used (fromBytes): %s", gas); + + assertEq(uint256(buf.length()), 8); + assertEq(uint256(buf.capacity()), 8); + assertEq(keccak256(buf.toBytes()), keccak256(data)); + } + + function testSetLength() public { + Buffer buf = Buffer_.allocate(32); + + uint256 gas = gasleft(); + buf._setLengthUnchecked(8); + gas = gas - gasleft(); + console.log("gas used (setLengthUnchecked): %s", gas); + + gas = gasleft(); + buf._setLength(16); + gas = gas - gasleft(); + console.log("gas used (setLength): %s", gas); + + assertEq(uint256(buf.length()), 16); + assertEq(uint256(buf.capacity()), 32); + } + + function testFailSetLength() public pure { + Buffer buf = Buffer_.allocate(32); + buf._setLength(33); + } + + function testAppend() public { + Buffer buf = Buffer_.allocate(32); + bytes memory data1 = bytes.concat(bytes8(0x0102030405060708)); + bytes memory data2 = bytes.concat(bytes8(0x090a0b0c0d0e0f10)); + + uint256 gas = gasleft(); + buf._appendUnchecked(data1); + gas = gas - gasleft(); + console.log("gas used (append unchecked): %s", gas); + + gas = gasleft(); + buf.append(data2); + gas = gas - gasleft(); + console.log("gas used (append): %s", gas); + + assertEq(uint256(buf.length()), 16); + } + + function testToBytes() public { + Buffer buf = Buffer_.allocate(32); + bytes memory data1 = bytes.concat(bytes8(0x0102030405060708)); + bytes memory data2 = bytes.concat(bytes8(0x090a0b0c0d0e0f10)); + bytes memory data = bytes.concat(data1, data2); + + buf.append(data1); + buf.append(data2); + + uint256 gas = gasleft(); + bytes memory bufferData = buf.toBytes(); + gas = gas - gasleft(); + console.log("gas used (toBytes): %s", gas); + + assertEq(keccak256(bufferData), keccak256(data)); + } + + function testRead() public { + Buffer buf = Buffer_.allocate(32); + bytes memory data = bytes.concat(bytes8(0x0102030405060708)); + buf.append(data); + + uint256 gas = gasleft(); + bytes32 value = buf.read32(4); + gas = gas - gasleft(); + console.log("gas used (read32): %s", gas); + + assertEq(value, bytes32(bytes4(0x05060708))); + + gas = gasleft(); + bytes8 value2 = buf.read8(3); + gas = gas - gasleft(); + console.log("gas used (read8): %s", gas); + + assertEq(value2, bytes8(bytes5(0x0405060708))); + + gas = gasleft(); + bytes1 value3 = buf.read1(7); + gas = gas - gasleft(); + console.log("gas used (read1): %s", gas); + + assertEq(value3, bytes1(0x08)); + } + + function testToArrayUint32() public { + Buffer buf = Buffer_.allocate(32); + bytes memory data1 = bytes.concat(bytes8(0x0102030405060708)); + buf.append(data1); + + uint256 gas = gasleft(); + uint256 arrayPtr = buf.toArray(4); + gas = gas - gasleft(); + console.log("gas used (toArray uint32[]): %s", gas); + uint32[] memory arr = Cast.toUint32Array(arrayPtr); + + assertEq(arr.length, 2); + assertEq(arr[0], 0x01020304); + assertEq(arr[1], 0x05060708); + } + + function testToArrayUint256() public { + Buffer buf = Buffer_.allocate(8); + bytes memory data = bytes.concat(bytes8(0x0102030405060708)); + buf.append(data); + + uint256 gas = gasleft(); + uint256 arrayPtr = buf.toArray(4); + gas = gas - gasleft(); + console.log("gas used (toArray uint256[]): %s", gas); + uint256[] memory arr = Cast.toUint256Array(arrayPtr); + + assertEq(arr.length, 2); + assertEq(arr[0], 0x01020304); + assertEq(arr[1], 0x05060708); + } + + function testToArrayUint256TrailingData() public { + Buffer buf = Buffer_.allocate(10); + bytes memory data = bytes.concat(bytes8(0x0102030405060708), bytes2(0x090a)); + buf.append(data); + uint256 arrayPtr = buf.toArray(4); + uint256[] memory arr = Cast.toUint256Array(arrayPtr); + + // The trailing bytes2 data is ignored because it doesn't align with one `elementSize` + assertEq(arr.length, 2); + assertEq(arr[0], 0x01020304); + assertEq(arr[1], 0x05060708); + } + + function testSlice() public { + Buffer buf = Buffer_.allocate(32); + bytes memory data = bytes.concat(bytes8(0x0102030405060708)); + buf.append(data); + + uint256 gas = gasleft(); + bytes memory slice = buf.slice(4, 4); + gas = gas - gasleft(); + console.log("gas used (slice): %s", gas); + + assertEq(uint256(slice.length), 4); + assertEq(keccak256(slice), keccak256(bytes.concat(bytes4(0x05060708)))); + } +} From 59675facc03290b2ab9b48ac1da6bc7f4bb5756e Mon Sep 17 00:00:00 2001 From: alvrs Date: Wed, 25 Jan 2023 18:45:15 +0100 Subject: [PATCH 14/82] feat(solecs): wip data model - add low level storage library --- packages/solecs/v2/Buffer.sol | 48 +++++- packages/solecs/v2/Storage.sol | 153 ++++++++++++++++++ packages/solecs/v2/Storage2.sol | 221 ++++++++++++++++++++++++++ packages/solecs/v2/Utils.sol | 19 +++ packages/solecs/v2/test/Buffer.t.sol | 24 ++- packages/solecs/v2/test/Storage.t.sol | 71 +++++++++ 6 files changed, 528 insertions(+), 8 deletions(-) create mode 100644 packages/solecs/v2/Storage.sol create mode 100644 packages/solecs/v2/Storage2.sol create mode 100644 packages/solecs/v2/test/Storage.t.sol diff --git a/packages/solecs/v2/Buffer.sol b/packages/solecs/v2/Buffer.sol index 14439d9649..077b0fd97b 100644 --- a/packages/solecs/v2/Buffer.sol +++ b/packages/solecs/v2/Buffer.sol @@ -22,6 +22,7 @@ library Buffer_ { assembly { let buf := mload(0x40) // free memory pointer mstore(0x40, add(buf, add(32, capacity))) // 32 bytes for the buffer header, plus the length of the buffer + mstore(buf, 0) // initialize length to 0 (memory is not cleared by default) ptr := add(buf, 32) // ptr to first data byte } @@ -47,7 +48,7 @@ library BufferUtils { uint256 constant MASK_CAPACITY = uint256(type(uint128).max); uint256 constant MASK_PTR = uint256(type(uint128).max) << 128; - error Buffer_Overflow(); + error Buffer_Overflow(uint256 capacity, uint256 requestedLength); /** * @dev Returns the pointer to the start of an in-memory buffer. @@ -104,14 +105,47 @@ library BufferUtils { */ function append(Buffer self, bytes memory data) internal pure { uint128 _newLength = length(self) + uint128(data.length); - _checkCapacity(self, _newLength); - _appendUnchecked(self, data); + checkCapacity(self, _newLength); + appendUnchecked(self, data); + } + + /** + * @dev Appends _dataLength of the given bytes32 to the buffer (checking for overflows) + */ + function append( + Buffer self, + bytes32 data, + uint128 _dataLength + ) internal pure { + uint128 _newLength = length(self) + _dataLength; + checkCapacity(self, _newLength); + appendUnchecked(self, data, _dataLength); + } + + /** + * @dev Appends _dataLength of the given bytes32 to the buffer (without checking for overflows) + */ + function appendUnchecked( + Buffer self, + bytes32 data, + uint128 _dataLength + ) internal pure { + uint256 _ptr = ptr(self); + uint128 _length = length(self); + + // Update the current buffer length + _setLengthUnchecked(self, _length + _dataLength); + + // Copy over given data to the buffer + assembly { + mstore(add(_ptr, _length), data) + } } /** * @dev Appends the given bytes to the buffer (without checking for overflows) */ - function _appendUnchecked(Buffer self, bytes memory data) internal pure { + function appendUnchecked(Buffer self, bytes memory data) internal pure { uint256 _ptr = ptr(self); uint128 _dataLength = uint128(data.length); uint128 _length = length(self); @@ -188,7 +222,7 @@ library BufferUtils { * @dev Modify buffer length (checking capacity) */ function _setLength(Buffer self, uint128 _length) internal pure { - _checkCapacity(self, _length); + checkCapacity(self, _length); _setLengthUnchecked(self, _length); } @@ -205,9 +239,9 @@ library BufferUtils { /** * @dev Check if the buffer has enough capacity to store a given length. */ - function _checkCapacity(Buffer self, uint128 _length) internal pure { + function checkCapacity(Buffer self, uint128 _length) internal pure { if (capacity(self) < _length) { - revert Buffer_Overflow(); + revert Buffer_Overflow(capacity(self), _length); } } } diff --git a/packages/solecs/v2/Storage.sol b/packages/solecs/v2/Storage.sol new file mode 100644 index 0000000000..286a31f81a --- /dev/null +++ b/packages/solecs/v2/Storage.sol @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import { Utils } from "./Utils.sol"; +import { Bytes } from "./Bytes.sol"; +import "./Buffer.sol"; + +library Storage { + /** + * @dev Write raw bytes to storage at the given storagePointer and offset (keeping the rest of the word intact) + * TODO: this implementation is optimized for readability, but not very gas efficient. We should optimize this using assembly once we've settled on a spec. + */ + function write( + uint256 storagePointer, + uint256 offset, + bytes memory data + ) internal { + uint256 numWords = Utils.divCeil(data.length + offset, 32); + uint256 bytesWritten; + + for (uint256 i; i < numWords; i++) { + // If this is the first word, and there is an offset, apply a mask to beginning + if ((i == 0 && offset > 0)) { + uint256 _length = data.length > 32 ? 32 - offset : data.length; // // the number of bytes to write + _writePartialWord( + storagePointer, // the word to update + offset, // the offset in bytes to start writing + _length, + bytes32(data) // Pass the first 32 bytes of the data + ); + bytesWritten += _length; + // If this is the last word, and there is a partial word, apply a mask to the end + } else if (i == numWords - 1 && (data.length + offset) % 32 > 0) { + _writePartialWord( + storagePointer + i, // the word to update + 0, // the offset in bytes to start writing + (data.length + offset) % 32, // the number of bytes to write + Bytes.slice32(data, bytesWritten) // the data to write + ); + + // Else, just write the word + } else { + _writeWord(storagePointer + i, Bytes.slice32(data, bytesWritten)); + bytesWritten += 32; + } + } + } + + function write(uint256 storagePointer, bytes memory data) internal { + write(storagePointer, 0, data); + } + + /** + * @dev Read raw bytes from storage at the given storagePointer, offset, and length + */ + function read( + uint256 storagePointer, + uint256 offset, + uint256 length + ) internal view returns (bytes memory) { + Buffer buf = Buffer_.allocate(uint128(length)); + + uint256 numWords = Utils.divCeil(length + offset, 32); + uint256 _length; + + for (uint256 i; i < numWords; i++) { + // If this is the first word, and there is an offset, apply a mask to beginning (and possibly the end if length + offset is less than 32) + if ((i == 0 && offset > 0)) { + _length = length > 32 ? 32 - offset : length; // the number of bytes to read + buf.appendUnchecked( + _loadPartialWord( + storagePointer, // the slot to start loading from + offset, // the offset in bytes to start reading from + _length + ), + uint128(_length) + ); + + // If this is the last word, and there is a partial word, apply a mask to the end + } else if (i == numWords - 1 && (length + offset) % 32 > 0) { + _length = (length + offset) % 32; // the relevant length of the trailing word + buf.appendUnchecked( + _loadPartialWord( + storagePointer + i, // the word to read from + 0, // the offset in bytes to start reading from + _length + ), + uint128(_length) + ); + + // Else, just read the word + } else { + buf.appendUnchecked(_loadWord(storagePointer + i), 32); + } + } + + return buf.toBytes(); + } + + function read(uint256 storagePointer) internal view returns (bytes32) { + return _loadWord(storagePointer); + } + + /** + * @dev Load a full word from storage into memory + */ + function _loadWord(uint256 storagePointer) internal view returns (bytes32 data) { + assembly { + data := sload(storagePointer) + } + } + + /** + * @dev Load a partial word from storage into memory + */ + function _loadPartialWord( + uint256 storagePointer, + uint256 offset, + uint256 length + ) internal view returns (bytes32) { + // Load current value from storage + bytes32 storageValue; + assembly { + storageValue := sload(storagePointer) + } + + // create a mask for the bits we want to update + return (storageValue << (offset * 8)) & bytes32(Utils.leftMask(length * 8)); + } + + function _writeWord(uint256 storagePointer, bytes32 data) internal { + assembly { + sstore(storagePointer, data) + } + } + + function _writePartialWord( + uint256 storagePointer, + uint256 offset, // in bytes + uint256 length, // in bytes + bytes32 data + ) internal { + bytes32 current; + assembly { + current := sload(storagePointer) + } + bytes32 mask = bytes32(Utils.leftMask(length * 8) >> (offset * 8)); // create a mask for the bits we want to update + bytes32 updated = (current & ~mask) | ((data >> (offset * 8)) & mask); // apply mask to data + assembly { + sstore(storagePointer, updated) + } + } +} diff --git a/packages/solecs/v2/Storage2.sol b/packages/solecs/v2/Storage2.sol new file mode 100644 index 0000000000..26ed236811 --- /dev/null +++ b/packages/solecs/v2/Storage2.sol @@ -0,0 +1,221 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +/** + * @author @dk1a + * from https://github.com/latticexyz/mud/blob/c4cae23ac0284d6d475b683ad145daaf748e910e/packages/solecs/v2/storage.sol + */ + +import { Utils } from "./Utils.sol"; + +library Storage { + function write(uint256 slotDest, bytes memory data) internal { + write(slotDest, 0, true, data); + } + + function write( + uint256 slotDest, + uint256 slotByteOffset, + bytes memory data + ) internal { + write(slotDest, slotByteOffset, true, data); + } + + function write( + uint256 slotDest, + uint256 slotByteOffset, + bool safeTail, + bytes memory data + ) internal { + // slots are measured in words, so a byte offset is needed for data within the word + if (slotByteOffset > 0) { + unchecked { + slotDest += slotByteOffset / 32; + slotByteOffset %= 32; + } + } + + uint256 length = data.length; + uint256 ptrSrc; + assembly { + // skip length + ptrSrc := add(data, 0x20) + } + + // copy unaligned prefix if necessary + if (slotByteOffset > 0) { + uint256 mask = Utils.leftMask(length * 8); + // this makes a middle mask that will have 0s on the left and *might* have 0s on the right + mask >>= slotByteOffset * 8; + + /// @solidity memory-safe-assembly + assembly { + sstore( + slotDest, + or( + // store the middle part + and(mload(ptrSrc), mask), + // preserve the surrounding parts + and(sload(slotDest), not(mask)) + ) + ) + } + + uint256 prefixLength; + // safe because of `slotByteOffset %= 32` at the start + unchecked { + prefixLength = 32 - slotByteOffset; + } + // return if done + if (length <= prefixLength) return; + + slotDest += 1; + // safe because of `length <= prefixLength` earlier + unchecked { + ptrSrc += prefixLength; + length -= prefixLength; + } + } + + // copy 32-byte chunks + while (length >= 32) { + /// @solidity memory-safe-assembly + assembly { + sstore(slotDest, mload(ptrSrc)) + } + slotDest += 1; + // safe because total addition will be <= length (ptr+len is implicitly safe) + unchecked { + ptrSrc += 32; + length -= 32; + } + } + + // return if nothing is left + if (length == 0) return; + + // copy the 0-31 length tail + if (safeTail) { + // preserve the trailing bytes after the tail + + uint256 mask = Utils.leftMask(length * 8); + /// @solidity memory-safe-assembly + assembly { + sstore( + slotDest, + or( + // store the left part + and(mload(ptrSrc), mask), + // preserve the right part + and(sload(slotDest), not(mask)) + ) + ) + } + } else { + // overwrite the trailing bytes after the tail with garbage from memory + // (this is fine only at the end of a sparse storage slot) + + /// @solidity memory-safe-assembly + assembly { + sstore(slotDest, mload(ptrSrc)) + } + } + } + + function read(uint256 slotSrc) internal view returns (bytes32 data) { + assembly { + data := sload(slotSrc) + } + } + + function read( + uint256 slotSrc, + uint256 slotByteOffset, + uint256 length + ) internal view returns (bytes memory data) { + // slots are measured in words, so a byte offset is needed for data within the word + if (slotByteOffset > 0) { + unchecked { + slotSrc += slotByteOffset / 32; + slotByteOffset %= 32; + } + } + + data = new bytes(length); + + uint256 ptrDest; + assembly { + // skip length + ptrDest := add(data, 0x20) + } + + uint256 mask; + // copy unaligned prefix if necessary + if (slotByteOffset > 0) { + mask = Utils.leftMask(length * 8); + // this makes a middle mask that will have 0s on the left and *might* have 0s on the right + mask >>= slotByteOffset * 8; + + /// @solidity memory-safe-assembly + assembly { + mstore( + ptrDest, + or( + // store the middle part + and(sload(slotSrc), mask), + // preserve the surrounding parts + and(mload(ptrDest), not(mask)) + ) + ) + } + + uint256 prefixLength; + // safe because of `slotByteOffset %= 32` revert at the start + unchecked { + prefixLength = 32 - slotByteOffset; + } + // return if done + if (length <= prefixLength) return data; + + slotSrc += 1; + // safe because of `length <= prefixLength` earlier + unchecked { + ptrDest += prefixLength; + length -= prefixLength; + } + } + + // copy 32-byte chunks + while (length >= 32) { + /// @solidity memory-safe-assembly + assembly { + mstore(ptrDest, sload(slotSrc)) + } + slotSrc += 1; + // safe because total addition will be <= length (ptr+len is implicitly safe) + unchecked { + ptrDest += 32; + length -= 32; + } + } + + // return if nothing is left + if (length == 0) return data; + + // copy the 0-31 length tail + // (always preserve the trailing bytes after the tail, an extra mload is cheap) + mask = Utils.leftMask(length * 8); + /// @solidity memory-safe-assembly + assembly { + mstore( + ptrDest, + or( + // store the left part + and(sload(slotSrc), mask), + // preserve the right part + and(mload(ptrDest), not(mask)) + ) + ) + } + } +} diff --git a/packages/solecs/v2/Utils.sol b/packages/solecs/v2/Utils.sol index d896c5e58c..577914fc91 100644 --- a/packages/solecs/v2/Utils.sol +++ b/packages/solecs/v2/Utils.sol @@ -5,4 +5,23 @@ library Utils { function divCeil(uint256 a, uint256 b) internal pure returns (uint256) { return a / b + (a % b == 0 ? 0 : 1); } + + /** + * Adapted from https://github.com/dk1a/solidity-stringutils/blob/main/src/utils/mem.sol#L149-L167 + * @dev Left-aligned bit mask (e.g. for partial mload/mstore). + * For length >= 32 returns type(uint256).max + * + * length 0: 0x000000...000000 + * length 1: 0xff0000...000000 + * length 2: 0xffff00...000000 + * ... + * length 30: 0xffffff...ff0000 + * length 31: 0xffffff...ffff00 + * length 32+: 0xffffff...ffffff + */ + function leftMask(uint256 bitLength) internal pure returns (uint256) { + unchecked { + return ~(type(uint256).max >> (bitLength)); + } + } } diff --git a/packages/solecs/v2/test/Buffer.t.sol b/packages/solecs/v2/test/Buffer.t.sol index babfc5f52a..0da64587e4 100644 --- a/packages/solecs/v2/test/Buffer.t.sol +++ b/packages/solecs/v2/test/Buffer.t.sol @@ -72,7 +72,7 @@ contract BufferTest is DSTestPlus { bytes memory data2 = bytes.concat(bytes8(0x090a0b0c0d0e0f10)); uint256 gas = gasleft(); - buf._appendUnchecked(data1); + buf.appendUnchecked(data1); gas = gas - gasleft(); console.log("gas used (append unchecked): %s", gas); @@ -82,6 +82,28 @@ contract BufferTest is DSTestPlus { console.log("gas used (append): %s", gas); assertEq(uint256(buf.length()), 16); + assertEq(buf.read8(0), bytes8(0x0102030405060708)); + assertEq(buf.read8(8), bytes8(0x090a0b0c0d0e0f10)); + } + + function testAppendFixed() public { + Buffer buf = Buffer_.allocate(32); + bytes32 data1 = bytes32(bytes8(0x0102030405060708)); + bytes32 data2 = bytes32(bytes8(0x090a0b0c0d0e0f10)); + + uint256 gas = gasleft(); + buf.appendUnchecked(data1, 8); + gas = gas - gasleft(); + console.log("gas used (append unchecked): %s", gas); + + gas = gasleft(); + buf.append(data2, 8); + gas = gas - gasleft(); + console.log("gas used (append): %s", gas); + + assertEq(uint256(buf.length()), 16); + assertEq(buf.read8(0), bytes8(0x0102030405060708)); + assertEq(buf.read8(8), bytes8(0x090a0b0c0d0e0f10)); } function testToBytes() public { diff --git a/packages/solecs/v2/test/Storage.t.sol b/packages/solecs/v2/test/Storage.t.sol new file mode 100644 index 0000000000..316eb254d6 --- /dev/null +++ b/packages/solecs/v2/test/Storage.t.sol @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import { console } from "forge-std/console.sol"; +import { DSTestPlus } from "solmate/test/utils/DSTestPlus.sol"; +import { Cast } from "../Cast.sol"; +import { Storage } from "../Storage.sol"; +import { Utils } from "../Utils.sol"; +import { Bytes } from "../Bytes.sol"; + +contract StorageTest is DSTestPlus { + function testWriteRead() public { + bytes memory data1 = bytes.concat( + bytes1(0x01), + bytes32(0x0200000000000000000000000000000000000000000000000000000000000003), + bytes1(0x04) + ); + + bytes memory originalDataFirstSlot = bytes.concat( + bytes32(0x42000000000000000000000000000000000000000000000000000000000069FF) + ); + bytes memory originalDataLastSlot = bytes.concat( + bytes32(0xFF42000000000000000000000000000000000000000000000000000000000069) + ); + + uint256 storagePointer = uint256(keccak256("some location")); + uint256 storagePointerTwoSlotsAfter = storagePointer + 2; + + // First write some data to storage at the target slot and two slots after the target slot + uint256 gas = gasleft(); + Storage.write(storagePointer, originalDataFirstSlot); + gas = gas - gasleft(); + console.log("gas used (write, %s slots): %s", Utils.divCeil(originalDataFirstSlot.length, 32), gas); + + gas = gasleft(); + Storage.write(storagePointerTwoSlotsAfter, originalDataLastSlot); + gas = gas - gasleft(); + console.log("gas used (write, %s slots): %s", Utils.divCeil(originalDataLastSlot.length, 32), gas); + + // Then set the target slot, partially overwriting the first and third slot, but using safeTrail and offset + gas = gasleft(); + Storage.write(storagePointer, 31, data1); + gas = gas - gasleft(); + console.log("gas used (write, %s slots): %s", Utils.divCeil(data1.length + 31, 32), gas); + + // Assert the first slot has the correct value + assertEq(Storage.read(storagePointer), bytes32(0x4200000000000000000000000000000000000000000000000000000000006901)); + + // Assert the second slot has the correct value + assertEq( + Storage.read(storagePointer + 1), + bytes32(0x0200000000000000000000000000000000000000000000000000000000000003) + ); + + // Assert that the trailing slot has the correct value + assertEq( + Storage.read(storagePointerTwoSlotsAfter), + bytes32(0x0442000000000000000000000000000000000000000000000000000000000069) + ); + + // Assert we can read the correct partial value from storage + gas = gasleft(); + bytes memory data = Storage.read(storagePointer, 31, 34); + gas = gas - gasleft(); + console.log("gas used (read, %s slots (warm)): %s", Utils.divCeil(data.length + 31, 32), gas); + assertEq(Bytes.slice1(data, 0), bytes1(0x01)); + assertEq(Bytes.slice32(data, 1), bytes32(0x0200000000000000000000000000000000000000000000000000000000000003)); + assertEq(Bytes.slice1(data, 33), bytes1(0x04)); + assertEq(keccak256(data), keccak256(data1)); + } +} From e769b4f1880e66bd10c79ed18c999f205ff95321 Mon Sep 17 00:00:00 2001 From: alvrs Date: Wed, 25 Jan 2023 19:06:05 +0100 Subject: [PATCH 15/82] refactor(solecs): wip data model - use Storage lib in StoreCore --- packages/solecs/v2/Storage.sol | 52 ++++++++++++-- packages/solecs/v2/StoreCore.sol | 90 ++----------------------- packages/solecs/v2/test/StoreCore.t.sol | 57 ---------------- 3 files changed, 52 insertions(+), 147 deletions(-) diff --git a/packages/solecs/v2/Storage.sol b/packages/solecs/v2/Storage.sol index 286a31f81a..a8bd9e26fb 100644 --- a/packages/solecs/v2/Storage.sol +++ b/packages/solecs/v2/Storage.sol @@ -6,6 +6,30 @@ import { Bytes } from "./Bytes.sol"; import "./Buffer.sol"; library Storage { + function write(bytes32 storagePointer, bytes memory data) internal { + write(uint256(storagePointer), 0, data); + } + + function write(uint256 storagePointer, bytes memory data) internal { + write(storagePointer, 0, data); + } + + function write(bytes32 storagePointer, bytes32 data) internal { + _writeWord(uint256(storagePointer), data); + } + + function write(uint256 storagePointer, bytes32 data) internal { + _writeWord(storagePointer, data); + } + + function write( + bytes32 storagePointer, + uint256 offset, + bytes memory data + ) internal { + write(uint256(storagePointer), offset, data); + } + /** * @dev Write raw bytes to storage at the given storagePointer and offset (keeping the rest of the word intact) * TODO: this implementation is optimized for readability, but not very gas efficient. We should optimize this using assembly once we've settled on a spec. @@ -46,8 +70,28 @@ library Storage { } } - function write(uint256 storagePointer, bytes memory data) internal { - write(storagePointer, 0, data); + function read(bytes32 storagePointer) internal view returns (bytes32) { + return _loadWord(uint256(storagePointer)); + } + + function read(uint256 storagePointer) internal view returns (bytes32) { + return _loadWord(storagePointer); + } + + function read(bytes32 storagePointer, uint256 length) internal view returns (bytes memory) { + return read(uint256(storagePointer), 0, length); + } + + function read(uint256 storagePointer, uint256 length) internal view returns (bytes memory) { + return read(storagePointer, 0, length); + } + + function read( + bytes32 storagePointer, + uint256 offset, + uint256 length + ) internal view returns (bytes memory) { + return read(uint256(storagePointer), offset, length); } /** @@ -97,10 +141,6 @@ library Storage { return buf.toBytes(); } - function read(uint256 storagePointer) internal view returns (bytes32) { - return _loadWord(storagePointer); - } - /** * @dev Load a full word from storage into memory */ diff --git a/packages/solecs/v2/StoreCore.sol b/packages/solecs/v2/StoreCore.sol index 987b9a4966..7a72206aef 100644 --- a/packages/solecs/v2/StoreCore.sol +++ b/packages/solecs/v2/StoreCore.sol @@ -3,6 +3,7 @@ pragma solidity >=0.8.0; import { Utils } from "./Utils.sol"; import { Bytes } from "./Bytes.sol"; import { SchemaType, getByteLength } from "./Types.sol"; +import { Storage } from "./Storage.sol"; import { console } from "forge-std/console.sol"; library StoreCore { @@ -37,7 +38,7 @@ library StoreCore { bytes32[] memory key = new bytes32[](1); key[0] = table; bytes32 location = _getLocation(_schemaTable, key); - _setDataUnchecked(location, 0, bytes.concat(schema)); + Storage.write(location, schema); } /** @@ -47,8 +48,7 @@ library StoreCore { bytes32[] memory key = new bytes32[](1); key[0] = table; bytes32 location = _getLocation(_schemaTable, key); - bytes memory blob = _getDataUnchecked(location, 32); - return bytes32(blob); + return Storage.read(location); } /************************************************************************ @@ -74,7 +74,7 @@ library StoreCore { // Store the provided value in storage bytes32 location = _getLocation(table, key); - _setDataUnchecked(location, 0, data); + Storage.write(location, data); // Emit event to notify indexers emit StoreUpdate(table, key, 0, 0, data); @@ -95,7 +95,7 @@ library StoreCore { // Store the provided value in storage bytes32 location = _getLocation(table, key); uint256 offset = _getDataOffset(schema, fieldIndex); - _setDataUnchecked(location, offset, data); + Storage.write(location, offset, data); // Emit event to notify indexers emit StoreUpdate(table, key, 0, 0, data); @@ -171,7 +171,7 @@ library StoreCore { ) internal view returns (bytes memory) { // Load the data from storage bytes32 location = _getLocation(table, key); - return _getDataUnchecked(location, _getSchemaLength(schema)); + return Storage.read(location, _getSchemaLength(schema)); } /************************************************************************ @@ -200,84 +200,6 @@ library StoreCore { } } - function _setPartialWord( - bytes32 location, - uint256 offset, // in bytes - uint256 length, // in bytes - bytes32 data - ) internal { - bytes32 current; - assembly { - current := sload(location) - } - bytes32 mask = bytes32(((1 << (length * 8)) - 1) << (256 - length * 8 - offset * 8)); // create a mask for the bits we want to update - console.logBytes32(mask); - bytes32 updated = (current & ~mask) | ((data >> (offset * 8)) & mask); // apply mask to data - console.logBytes32(updated); - assembly { - sstore(location, updated) - } - } - - /** - * Write raw bytes to storage at the given location - * TODO: this implementation is optimized for readability, but not very gas efficient. We should optimize this using assembly once we've settled on a spec. - */ - function _setDataUnchecked( - bytes32 location, - uint256 offset, - bytes memory data - ) internal { - uint256 numWords = Utils.divCeil(data.length, 32); - - for (uint256 i; i < numWords; i++) { - // If this is the first word, and there is an offset, apply a mask to beginning - if ((i == 0 && offset > 0)) { - _setPartialWord( - location, // the word to update - offset, // the offset in bytes to start writing - data.length > 32 ? 32 - offset : data.length, // the number of bytes to write - bytes32(data) - ); - - // If this is the last word, and there is a partial word, apply a mask to the end - } else if (i == numWords - 1 && data.length % 32 > 0) { - _setPartialWord( - bytes32(uint256(location) + i * 32), // the word to update - 0, // the offset in bytes to start writing - data.length % 32, // the number of bytes to write - Bytes.slice32(data, i * 32) // the data to write - ); - - // Otherwise, just write the word - } else { - _setFullWord(bytes32(uint256(location) + i * 32), Bytes.slice32(data, i * 32)); - } - } - } - - /** - * Read raw bytes from storage at the given location and length in bytess - * TODO: implement offset - */ - function _getDataUnchecked(bytes32 location, uint256 length) internal view returns (bytes memory data) { - data = new bytes(length); - // load data from storage into memory - assembly { - for { - let i := 0 - } lt(i, length) { - i := add(i, 0x20) // increment by 32 since we are loading 32 bytes at a time - } { - mstore(add(data, add(0x20, i)), sload(add(location, i))) - } - } - - // remove offset - - // remove unused trailing data - } - /** * Get the length of the data for the given schema */ diff --git a/packages/solecs/v2/test/StoreCore.t.sol b/packages/solecs/v2/test/StoreCore.t.sol index 084e65277c..4fc454a604 100644 --- a/packages/solecs/v2/test/StoreCore.t.sol +++ b/packages/solecs/v2/test/StoreCore.t.sol @@ -53,63 +53,6 @@ contract StoreCoreTest is DSTestPlus { assertEq(uint8(StoreCore._getSchemaTypeAtIndex(encodedSchema, 4)), uint8(SchemaType.Uint256)); } - function testSetAndGetDataUncheckedOneSlot() public { - bytes32 location = keccak256("some location"); - bytes memory data = new bytes(32); - - data[0] = 0x01; - data[31] = 0x02; - - uint256 gas = gasleft(); - StoreCore._setDataUnchecked(location, 0, data); - gas = gas - gasleft(); - console.log("gas used (set): %s", gas); - - gas = gasleft(); - bytes memory loadedData = StoreCore._getDataUnchecked(location, data.length); - gas = gas - gasleft(); - console.log("gas used (get, warm): %s", gas); - - assertEq(bytes32(loadedData), bytes32(data)); - } - - function testSetAndGetDataUncheckedMultipleSlots() public { - bytes32 location = keccak256("some location"); - bytes memory data = abi.encode("this is some data spanning multiple words"); - - uint256 gas = gasleft(); - StoreCore._setDataUnchecked(location, 0, data); - gas = gas - gasleft(); - console.log("gas used (set, %s slots): %s", Utils.divCeil(data.length, 32), gas); - - gas = gasleft(); - bytes memory loadedData = StoreCore._getDataUnchecked(location, data.length); - gas = gas - gasleft(); - console.log("gas used (get, warm, %s slots): %s", Utils.divCeil(data.length, 32), gas); - - assertTrue(Bytes.equals(data, loadedData)); - } - - function testSetAndGetDataUncheckedOffset() public { - bytes32 location = keccak256("some location"); - bytes memory data = new bytes(16); - - data[0] = 0x01; - data[15] = 0x02; - - uint256 gas = gasleft(); - StoreCore._setDataUnchecked(location, 8, data); - gas = gas - gasleft(); - console.log("gas used (set): %s", gas); - - gas = gasleft(); - bytes memory loadedData = StoreCore._getDataUnchecked(location, data.length); - gas = gas - gasleft(); - console.log("gas used (get, warm): %s", gas); - - assertEq(bytes32(loadedData), bytes32(bytes.concat(bytes8(0), data))); - } - function testRegisterAndGetSchema() public { bytes32 schema = bytes32( bytes.concat( From 72c0439418747e165e2aaf98c9363c583420881d Mon Sep 17 00:00:00 2001 From: alvrs Date: Wed, 25 Jan 2023 19:58:49 +0100 Subject: [PATCH 16/82] refactor(solecs): wip data model - change schema encoding and naming --- packages/solecs/v2/IStore.sol | 10 +-- packages/solecs/v2/StoreCore.sol | 115 ++++++++++++++---------- packages/solecs/v2/Types.sol | 31 ++++++- packages/solecs/v2/World.sol | 4 +- packages/solecs/v2/test/StoreCore.t.sol | 15 ++-- 5 files changed, 113 insertions(+), 62 deletions(-) diff --git a/packages/solecs/v2/IStore.sol b/packages/solecs/v2/IStore.sol index 10d4d9f1a0..764eba241c 100644 --- a/packages/solecs/v2/IStore.sol +++ b/packages/solecs/v2/IStore.sol @@ -5,7 +5,7 @@ import { SchemaType } from "./Types.sol"; interface IStore { // note: the preimage of the tuple of keys used to index is part of the event, so it can be used by indexers - event StoreUpdate(bytes32 table, bytes32[] key, uint16 arrayIndex, uint8 fieldIndex, bytes data); + event StoreUpdate(bytes32 table, bytes32[] key, uint8 schemaIndex, bytes data); function registerSchema(bytes32 table, bytes32 schema) external; @@ -22,7 +22,7 @@ interface IStore { function setField( bytes32 table, bytes32[] memory key, - uint8 fieldIndex, + uint8 schemaIndex, bytes memory data ) external; @@ -39,7 +39,7 @@ interface IStore { bytes32 table, bytes32[] memory key, uint16 arrayIndex, - uint8 fieldIndex, + uint8 schemaIndex, bytes memory data ) external; @@ -50,7 +50,7 @@ interface IStore { function getField( bytes32 table, bytes32[] memory key, - uint8 fieldIndex + uint8 schemaIndex ) external view returns (bytes memory data); // Get full record of a single item at a given array index @@ -65,7 +65,7 @@ interface IStore { bytes32 table, bytes32[] memory key, uint16 arrayIndex, - uint8 fieldIndex + uint8 schemaIndex ) external view returns (bytes memory data); // If this function exists on the contract, it is a store diff --git a/packages/solecs/v2/StoreCore.sol b/packages/solecs/v2/StoreCore.sol index 7a72206aef..4e3c7f8add 100644 --- a/packages/solecs/v2/StoreCore.sol +++ b/packages/solecs/v2/StoreCore.sol @@ -2,13 +2,14 @@ pragma solidity >=0.8.0; import { Utils } from "./Utils.sol"; import { Bytes } from "./Bytes.sol"; -import { SchemaType, getByteLength } from "./Types.sol"; +import { SchemaType, getStaticByteLength } from "./Types.sol"; import { Storage } from "./Storage.sol"; import { console } from "forge-std/console.sol"; +import "./Buffer.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 StoreUpdate(bytes32 table, bytes32[] key, uint16 arrayIndex, uint8 fieldIndex, bytes data); + event StoreUpdate(bytes32 table, bytes32[] key, uint8 schemaIndex, bytes data); bytes32 constant _slot = keccak256("mud.store"); bytes32 constant _schemaTable = keccak256("mud.store.table.schema"); @@ -37,7 +38,7 @@ library StoreCore { // TODO: verify the table doesn't already exist bytes32[] memory key = new bytes32[](1); key[0] = table; - bytes32 location = _getLocation(_schemaTable, key); + bytes32 location = _getStaticDataLocation(_schemaTable, key); Storage.write(location, schema); } @@ -47,7 +48,7 @@ library StoreCore { function getSchema(bytes32 table) internal view returns (bytes32 schema) { bytes32[] memory key = new bytes32[](1); key[0] = table; - bytes32 location = _getLocation(_schemaTable, key); + bytes32 location = _getStaticDataLocation(_schemaTable, key); return Storage.read(location); } @@ -69,36 +70,36 @@ library StoreCore { // verify the value has the correct length for the table (based on the table's schema) // to prevent invalid data from being stored bytes32 schema = getSchema(table); - if (_getSchemaLength(schema) != data.length) - revert StoreCore_InvalidDataLength(_getSchemaLength(schema), data.length); + if (_getStaticDataLength(schema) != data.length) + revert StoreCore_InvalidDataLength(_getStaticDataLength(schema), data.length); // Store the provided value in storage - bytes32 location = _getLocation(table, key); + bytes32 location = _getStaticDataLocation(table, key); Storage.write(location, data); // Emit event to notify indexers - emit StoreUpdate(table, key, 0, 0, data); + emit StoreUpdate(table, key, 0, data); } function setField( bytes32 table, bytes32[] memory key, - uint8 fieldIndex, + uint8 schemaIndex, bytes memory data ) internal { // verify the value has the correct length for the field bytes32 schema = getSchema(table); - SchemaType schemaType = _getSchemaTypeAtIndex(schema, fieldIndex); - if (getByteLength(schemaType) != data.length) - revert StoreCore_InvalidDataLength(getByteLength(schemaType), data.length); + SchemaType schemaType = _getSchemaTypeAtIndex(schema, schemaIndex); + if (getStaticByteLength(schemaType) != data.length) + revert StoreCore_InvalidDataLength(getStaticByteLength(schemaType), data.length); // Store the provided value in storage - bytes32 location = _getLocation(table, key); - uint256 offset = _getDataOffset(schema, fieldIndex); + bytes32 location = _getStaticDataLocation(table, key); + uint256 offset = _getStaticDataOffset(schema, schemaIndex); Storage.write(location, offset, data); // Emit event to notify indexers - emit StoreUpdate(table, key, 0, 0, data); + emit StoreUpdate(table, key, schemaIndex, data); } function setArrayIndex( @@ -170,8 +171,8 @@ library StoreCore { bytes32 schema ) internal view returns (bytes memory) { // Load the data from storage - bytes32 location = _getLocation(table, key); - return Storage.read(location, _getSchemaLength(schema)); + bytes32 location = _getStaticDataLocation(table, key); + return Storage.read(location, _getStaticDataLength(schema)); } /************************************************************************ @@ -180,6 +181,35 @@ library StoreCore { * ************************************************************************/ + /** + * Encode the given schema into a single bytes32 + * - The first two bytes are the static length of the schema + * - The third byte is the number of static size fields + * - The fourth byte is the number of dynamic size fields + * - The remaining bytes are the schema types + */ + function encodeSchema(SchemaType[] memory _schema) internal pure returns (bytes32) { + if (_schema.length > 28) revert StoreCore_SchemaTooLong(); + uint16 length; + uint8 staticFields; + bytes memory schema = new bytes(32); + + for (uint256 i = 0; i < _schema.length; i++) { + uint16 staticByteLength = uint16(getStaticByteLength(_schema[i])); + if (staticByteLength > 0) staticFields++; + length += staticByteLength; + + schema[i + 4] = bytes1(uint8(_schema[i])); + } + + schema[0] = bytes1(bytes2(length)); // upper length byte + schema[1] = bytes1(uint8(length)); // lower length byte + schema[2] = bytes1(staticFields); // number of static fields + schema[3] = bytes1(uint8(_schema.length) - staticFields); // number of dynamic fields + + return bytes32(schema); + } + /************************************************************************ * * INTERNAL HELPER FUNCTIONS @@ -190,57 +220,48 @@ library StoreCore { * Compute the storage location based on table id and index tuple * TODO: provide different overloads for single key and some fixed length array keys for better devex */ - function _getLocation(bytes32 table, bytes32[] memory key) internal pure returns (bytes32) { + function _getStaticDataLocation(bytes32 table, bytes32[] memory key) internal pure returns (bytes32) { return keccak256(abi.encode(_slot, table, key)); } - function _setFullWord(bytes32 location, bytes32 data) internal { - assembly { - sstore(location, data) + /** + * Get storage offset for the given schema and (static length) index + * TODO: gas optimize + */ + function _getStaticDataOffset(bytes32 schema, uint8 schemaIndex) internal pure returns (uint256) { + uint256 offset = 0; // skip length + for (uint256 i = 0; i < schemaIndex; i++) { + offset += getStaticByteLength(_getSchemaTypeAtIndex(schema, i)); } + return offset; } /** - * Get the length of the data for the given schema + * Get the length of the static data for the given schema */ - function _getSchemaLength(bytes32 schema) internal pure returns (uint256) { + function _getStaticDataLength(bytes32 schema) internal pure returns (uint256) { return uint256(uint16(bytes2(schema))); } /** - * Get the offset of the data for the given schema at the given index - * TODO: gas optimize + * Get the number of static fields for the given schema */ - function _getDataOffset(bytes32 schema, uint8 fieldIndex) internal pure returns (uint256) { - uint256 offset = 2; // skip length - for (uint256 i = 0; i < fieldIndex; i++) { - offset += getByteLength(_getSchemaTypeAtIndex(schema, i)); - } - return offset; + function _getNumStaticFields(bytes32 schema) internal pure returns (uint8) { + return uint8(Bytes.slice1(schema, 2)); } /** - * Get the type of the data for the given schema at the given index + * Get the number of variable fields for the given schema */ - function _getSchemaTypeAtIndex(bytes32 schema, uint256 index) internal pure returns (SchemaType) { - return SchemaType(uint8(Bytes.slice1(schema, index + 2))); + function _getNumDynamicFields(bytes32 schema) internal pure returns (uint8) { + return uint8(Bytes.slice1(schema, 3)); } /** - * Encode the given schema into a single bytes32 - * TODO: gas optimize, replace bytes.concat with Buffer + * Get the type of the data for the given schema at the given index */ - function _encodeSchema(SchemaType[] memory _schema) internal pure returns (bytes32) { - if (_schema.length > 30) revert StoreCore_SchemaTooLong(); - uint16 length; - bytes memory schema; - - for (uint256 i = 0; i < _schema.length; i++) { - length += uint16(getByteLength(_schema[i])); - schema = bytes.concat(schema, bytes1(uint8(_schema[i]))); - } - - return bytes32(bytes.concat(bytes2(length), schema)); + function _getSchemaTypeAtIndex(bytes32 schema, uint256 index) internal pure returns (SchemaType) { + return SchemaType(uint8(Bytes.slice1(schema, index + 4))); } } diff --git a/packages/solecs/v2/Types.sol b/packages/solecs/v2/Types.sol index 20651e9814..e616b6c4fa 100644 --- a/packages/solecs/v2/Types.sol +++ b/packages/solecs/v2/Types.sol @@ -9,6 +9,7 @@ enum SchemaType { Uint32, Uint128, Uint256, + Uint32Array, Bytes4, Address } @@ -18,7 +19,7 @@ enum SchemaType { * (Because Solidity doesn't support constant arrays, we need to use a function) * TODO: add more types and make it more efficient (avoid linear search) */ -function getByteLength(SchemaType schemaType) pure returns (uint256) { +function getStaticByteLength(SchemaType schemaType) pure returns (uint256) { if (schemaType == SchemaType.Uint8) { return 1; } else if (schemaType == SchemaType.Uint16) { @@ -32,7 +33,33 @@ function getByteLength(SchemaType schemaType) pure returns (uint256) { } else if (schemaType == SchemaType.Address) { return 20; } else { - revert("Unsupported schema type"); + // Return 0 for all dynamic types + return 0; + } +} + +/** + * Returns true if the schema type has a fixed length + * (Because Solidity doesn't support constant arrays, we need to use a function) + * TODO: add more types and make it more efficient (avoid linear search) + */ +function hasStaticLength(SchemaType schemaType) pure returns (bool) { + if (schemaType == SchemaType.Uint8) { + return true; + } else if (schemaType == SchemaType.Uint16) { + return true; + } else if (schemaType == SchemaType.Uint32) { + return true; + } else if (schemaType == SchemaType.Uint128) { + return true; + } else if (schemaType == SchemaType.Uint256) { + return true; + } else if (schemaType == SchemaType.Bytes4) { + return true; + } else if (schemaType == SchemaType.Address) { + return true; + } else { + return false; } } diff --git a/packages/solecs/v2/World.sol b/packages/solecs/v2/World.sol index 17429d2c50..4ece390954 100644 --- a/packages/solecs/v2/World.sol +++ b/packages/solecs/v2/World.sol @@ -33,10 +33,10 @@ contract World is StoreView { function setField( bytes32 table, bytes32[] memory key, - uint8 fieldIndex, + uint8 schemaIndex, bytes memory data ) public override { - StoreCore.setField(table, key, fieldIndex, data); + StoreCore.setField(table, key, schemaIndex, data); } function registerSystem( diff --git a/packages/solecs/v2/test/StoreCore.t.sol b/packages/solecs/v2/test/StoreCore.t.sol index 4fc454a604..b16dff3a0d 100644 --- a/packages/solecs/v2/test/StoreCore.t.sol +++ b/packages/solecs/v2/test/StoreCore.t.sol @@ -9,13 +9,13 @@ import { Bytes } from "../Bytes.sol"; import { SchemaType } from "../Types.sol"; contract StoreCoreTest is DSTestPlus { - function testGetSchemaLength() public { + function testGetStaticDataLength() public { bytes32 schema = bytes32( bytes.concat(bytes2(uint16(3)), bytes1(uint8(SchemaType.Uint8)), bytes1(uint8(SchemaType.Uint16))) ); uint256 gas = gasleft(); - uint256 length = StoreCore._getSchemaLength(schema); + uint256 length = StoreCore._getStaticDataLength(schema); gas = gas - gasleft(); console.log("gas used: %s", gas); @@ -23,20 +23,21 @@ contract StoreCoreTest is DSTestPlus { } function testEncodeDecodeSchema() public { - SchemaType[] memory schema = new SchemaType[](5); + SchemaType[] memory schema = new SchemaType[](6); schema[0] = SchemaType.Uint8; // 1 byte schema[1] = SchemaType.Uint16; // 2 bytes schema[2] = SchemaType.Uint32; // 4 bytes schema[3] = SchemaType.Uint128; // 4 bytes schema[4] = SchemaType.Uint256; // 4 bytes + schema[5] = SchemaType.Uint32Array; // 0 bytes (because it's dynamic) uint256 gas = gasleft(); - bytes32 encodedSchema = StoreCore._encodeSchema(schema); + bytes32 encodedSchema = StoreCore.encodeSchema(schema); gas = gas - gasleft(); console.log("gas used (encode): %s", gas); gas = gasleft(); - uint256 length = StoreCore._getSchemaLength(encodedSchema); + uint256 length = StoreCore._getStaticDataLength(encodedSchema); gas = gas - gasleft(); console.log("gas used (get length): %s", gas); @@ -51,6 +52,8 @@ contract StoreCoreTest is DSTestPlus { assertEq(uint8(StoreCore._getSchemaTypeAtIndex(encodedSchema, 2)), uint8(SchemaType.Uint32)); assertEq(uint8(StoreCore._getSchemaTypeAtIndex(encodedSchema, 3)), uint8(SchemaType.Uint128)); assertEq(uint8(StoreCore._getSchemaTypeAtIndex(encodedSchema, 4)), uint8(SchemaType.Uint256)); + assertEq(StoreCore._getNumStaticFields(encodedSchema), 5); + assertEq(StoreCore._getNumDynamicFields(encodedSchema), 1); } function testRegisterAndGetSchema() public { @@ -150,7 +153,7 @@ contract StoreCoreTest is DSTestPlus { schemaTypes[0] = SchemaType.Uint128; schemaTypes[1] = SchemaType.Uint256; - bytes32 schema = StoreCore._encodeSchema(schemaTypes); + bytes32 schema = StoreCore.encodeSchema(schemaTypes); bytes32 table = keccak256("some.table"); StoreCore.registerSchema(table, schema); From 65411d8904aff8328a8c266dfdfb4eb09c39400f Mon Sep 17 00:00:00 2001 From: alvrs Date: Thu, 26 Jan 2023 12:56:52 +0100 Subject: [PATCH 17/82] feat(solecs): wip data model - add logic and tests to set and get dynamic data length --- packages/solecs/v2/Bytes.sol | 66 +++++++++ packages/solecs/v2/StoreCore.sol | 183 ++++++++++++++++++++---- packages/solecs/v2/Types.sol | 8 ++ packages/solecs/v2/test/Bytes.t.sol | 45 ++++++ packages/solecs/v2/test/StoreCore.t.sol | 61 +++++++- 5 files changed, 331 insertions(+), 32 deletions(-) diff --git a/packages/solecs/v2/Bytes.sol b/packages/solecs/v2/Bytes.sol index a4b86fb063..a6828fd2ff 100644 --- a/packages/solecs/v2/Bytes.sol +++ b/packages/solecs/v2/Bytes.sol @@ -4,6 +4,7 @@ pragma solidity >=0.8.0; import { console } from "forge-std/console.sol"; import { Utils } from "./Utils.sol"; import { SchemaType } from "./Types.sol"; +import { console } from "forge-std/console.sol"; library Bytes { error Bytes_InputTooShort(); @@ -187,6 +188,63 @@ library Bytes { return input; } + /************************************************************************ + * + * SET + * + ************************************************************************/ + + /** + * Overwrite a single byte of a `bytes32` value and return the new value. + */ + function setBytes1( + bytes32 input, + uint256 index, + bytes1 overwrite + ) internal pure returns (bytes32 output) { + bytes1 mask = 0xff; + assembly { + mask := shr(mul(8, index), mask) // create a mask by shifting 0xff right by index bytes + output := and(input, not(mask)) // zero out the byte at index + output := or(output, shr(mul(8, index), overwrite)) // set the byte at index + } + return output; + } + + /** + * Overwrite two bytes of a `bytes32` value and return the new value. + */ + function setBytes2( + bytes32 input, + uint256 index, + bytes2 overwrite + ) internal pure returns (bytes32 output) { + bytes2 mask = 0xffff; + assembly { + mask := shr(mul(8, index), mask) // create a mask by shifting 0xffff right by index bytes + output := and(input, not(mask)) // zero out the byte at index + output := or(output, shr(mul(8, index), overwrite)) // set the byte at index + } + return output; + } + + /** + * Overwrite four bytes of a `bytes32` value and return the new value. + */ + function setBytes4( + bytes32 input, + uint256 index, + bytes4 overwrite + ) internal view returns (bytes32 output) { + bytes4 mask = 0xffffffff; + assembly { + mask := shr(mul(8, index), mask) // create a mask by shifting 0xffffffff right by index bytes + output := and(input, not(mask)) // zero out the byte at index + output := or(output, shr(mul(8, index), overwrite)) // set the byte at index + } + return output; + } + /************************************************************************ * * SLICE @@ -285,6 +343,14 @@ library Bytes { return output; } + function slice2(bytes32 data, uint256 start) internal pure returns (bytes2) { + bytes2 output; + assembly { + output := shl(mul(8, start), data) + } + return output; + } + /** Slice bytes to bytes3 without copying data */ function slice3(bytes memory data, uint256 start) internal pure returns (bytes3) { bytes3 output; diff --git a/packages/solecs/v2/StoreCore.sol b/packages/solecs/v2/StoreCore.sol index 4e3c7f8add..6296d8411b 100644 --- a/packages/solecs/v2/StoreCore.sol +++ b/packages/solecs/v2/StoreCore.sol @@ -2,7 +2,7 @@ pragma solidity >=0.8.0; import { Utils } from "./Utils.sol"; import { Bytes } from "./Bytes.sol"; -import { SchemaType, getStaticByteLength } from "./Types.sol"; +import { SchemaType, getStaticByteLength, getElementByteLength } from "./Types.sol"; import { Storage } from "./Storage.sol"; import { console } from "forge-std/console.sol"; import "./Buffer.sol"; @@ -11,8 +11,8 @@ 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 StoreUpdate(bytes32 table, bytes32[] key, uint8 schemaIndex, bytes data); - bytes32 constant _slot = keccak256("mud.store"); - bytes32 constant _schemaTable = keccak256("mud.store.table.schema"); + bytes32 internal constant SLOT = keccak256("mud.store"); + bytes32 internal constant SCHEMA_TABLE = keccak256("mud.store.table.schema"); error StoreCore_SchemaTooLong(); error StoreCore_NotImplemented(); @@ -36,9 +36,10 @@ library StoreCore { */ function registerSchema(bytes32 table, bytes32 schema) internal { // TODO: verify the table doesn't already exist + // TODO: verify the schema is valid (no dynamic elements in static section, etc) bytes32[] memory key = new bytes32[](1); key[0] = table; - bytes32 location = _getStaticDataLocation(_schemaTable, key); + bytes32 location = _getStaticDataLocation(SCHEMA_TABLE, key); Storage.write(location, schema); } @@ -48,7 +49,7 @@ library StoreCore { function getSchema(bytes32 table) internal view returns (bytes32 schema) { bytes32[] memory key = new bytes32[](1); key[0] = table; - bytes32 location = _getStaticDataLocation(_schemaTable, key); + bytes32 location = _getStaticDataLocation(SCHEMA_TABLE, key); return Storage.read(location); } @@ -183,31 +184,36 @@ library StoreCore { /** * Encode the given schema into a single bytes32 - * - The first two bytes are the static length of the schema - * - The third byte is the number of static size fields - * - The fourth byte is the number of dynamic size fields - * - The remaining bytes are the schema types + * - 2 bytes static length of the schema + * - 1 byte for number of static size fields + * - 1 byte for number of dynamic size fields + * - 28 bytes for 28 schema types */ function encodeSchema(SchemaType[] memory _schema) internal pure returns (bytes32) { if (_schema.length > 28) revert StoreCore_SchemaTooLong(); uint16 length; uint8 staticFields; - bytes memory schema = new bytes(32); + bytes32 schema = bytes32(0); - for (uint256 i = 0; i < _schema.length; i++) { + // Compute the length of the schema and the number of static fields + // and store the schema types in the encoded schema + for (uint256 i = 0; i < _schema.length; ) { uint16 staticByteLength = uint16(getStaticByteLength(_schema[i])); if (staticByteLength > 0) staticFields++; length += staticByteLength; - - schema[i + 4] = bytes1(uint8(_schema[i])); + schema = Bytes.setBytes1(schema, i + 4, bytes1(uint8(_schema[i]))); + unchecked { + i++; + } } - schema[0] = bytes1(bytes2(length)); // upper length byte - schema[1] = bytes1(uint8(length)); // lower length byte - schema[2] = bytes1(staticFields); // number of static fields - schema[3] = bytes1(uint8(_schema.length) - staticFields); // number of dynamic fields + // Store total static length, and number of static and dynamic fields + schema = Bytes.setBytes1(schema, 0, bytes1(bytes2(length))); // upper length byte + schema = Bytes.setBytes1(schema, 1, bytes1(uint8(length))); // lower length byte + schema = Bytes.setBytes1(schema, 2, bytes1(staticFields)); // number of static fields + schema = Bytes.setBytes1(schema, 3, bytes1(uint8(_schema.length) - staticFields)); // number of dynamic fields - return bytes32(schema); + return schema; } /************************************************************************ @@ -216,12 +222,16 @@ library StoreCore { * ************************************************************************/ + ///////////////////////////////////////////////////////////////////////// + // STATIC DATA + ///////////////////////////////////////////////////////////////////////// + /** * Compute the storage location based on table id and index tuple * TODO: provide different overloads for single key and some fixed length array keys for better devex */ function _getStaticDataLocation(bytes32 table, bytes32[] memory key) internal pure returns (bytes32) { - return keccak256(abi.encode(_slot, table, key)); + return keccak256(abi.encode(SLOT, table, key)); } /** @@ -236,6 +246,13 @@ library StoreCore { return offset; } + /** + * Get the number of static fields for the given schema + */ + function _getNumStaticFields(bytes32 schema) internal pure returns (uint8) { + return uint8(Bytes.slice1(schema, 2)); + } + /** * Get the length of the static data for the given schema */ @@ -244,24 +261,138 @@ library StoreCore { } /** - * Get the number of static fields for the given schema + * Get the type of the data for the given schema at the given index */ - function _getNumStaticFields(bytes32 schema) internal pure returns (uint8) { - return uint8(Bytes.slice1(schema, 2)); + function _getSchemaTypeAtIndex(bytes32 schema, uint256 index) internal pure returns (SchemaType) { + return SchemaType(uint8(Bytes.slice1(schema, index + 4))); } + ///////////////////////////////////////////////////////////////////////// + // DYNAMIC DATA + ///////////////////////////////////////////////////////////////////////// + /** - * Get the number of variable fields for the given schema + * Get the number of dynamic length fields for the given schema */ function _getNumDynamicFields(bytes32 schema) internal pure returns (uint8) { return uint8(Bytes.slice1(schema, 3)); } /** - * Get the type of the data for the given schema at the given index + * Compute the storage location based on table id and index tuple */ - function _getSchemaTypeAtIndex(bytes32 schema, uint256 index) internal pure returns (SchemaType) { - return SchemaType(uint8(Bytes.slice1(schema, index + 4))); + function _getDynamicDataLocation( + bytes32 table, + bytes32[] memory key, + uint8 schemaIndex + ) internal pure returns (bytes32) { + return keccak256(abi.encode(SLOT, table, key, schemaIndex)); + } + + /** + * Compute the storage location for the length of the dynamic data + */ + function _getDynamicDataLengthLocation(bytes32 table, bytes32[] memory key) internal pure returns (bytes32) { + return keccak256(abi.encode(SLOT, table, key, "length")); + } + + /** + * Get the length of the dynamic data for the given schema and index + * TODO: add tests + */ + function _loadEncodedDynamicDataLength(bytes32 table, bytes32[] memory key) internal view returns (bytes32) { + // Load dynamic data length from storage + bytes32 dynamicSchemaLengthSlot = _getDynamicDataLengthLocation(table, key); + return Storage.read(dynamicSchemaLengthSlot); + } + + /** + * Decode the full dynamic data length (in bytes) from the given encoded lengths + * (first four bytes of encoded lengths) + */ + function _decodeDynamicDataTotalLength(bytes32 encodedLengths) internal pure returns (uint256) { + return uint256(uint32(bytes4(encodedLengths))); + } + + /** + * Decode the dynamic data length (in bytes) for the given schema and index from the given encoded lengths + * (two bytes per dynamic schema after the first four bytes) + */ + function _decodeDynamicDataLengthAtIndex( + bytes32 encodedLengths, + bytes32 schema, + uint8 schemaIndex + ) internal pure returns (uint256) { + // Compute dynamic schema index and offset into encoded lengths + uint8 dynamicSchemaIndex = schemaIndex - _getNumStaticFields(schema); + uint256 offset = 4 + dynamicSchemaIndex * 2; // (4 bytes total length, 2 bytes per dynamic schema) + + // Return dynamic data length in bytes for the given index + return uint256(uint16(Bytes.slice2(encodedLengths, offset))); + } + + /** + * Get the total length of the dynamic data (in bytes) + * (Note: if the length at a specific index is also required, it is recommended to use _loadEncodedDynamicDataLength explicitly) + */ + function _getDynamicDataTotalLength(bytes32 table, bytes32[] memory key) internal view returns (uint256) { + // Load dynamic data length from storage + bytes32 encodedLengths = _loadEncodedDynamicDataLength(table, key); + + // Return dynamic data length in bytes for the given index + return _decodeDynamicDataTotalLength(encodedLengths); + } + + /** + * Get the length of the dynamic data (in bytes) for the given schema and index + * (Note: if the total length is also required, it is recommended to use _loadEncodedDynamicDataLength explicitly) + */ + function _getDynamicDataLengthAtIndex( + bytes32 table, + bytes32[] memory key, + bytes32 schema, + uint8 schemaIndex + ) internal view returns (uint256) { + // Load dynamic data length from storage + bytes32 encodedLengths = _loadEncodedDynamicDataLength(table, key); + + // Return dynamic data length in bytes for the given index + return _decodeDynamicDataLengthAtIndex(encodedLengths, schema, schemaIndex); + } + + /** + * Set the length of the dynamic data (in bytes) for the given schema and index + */ + function _setDynamicDataLengthAtIndex( + bytes32 table, + bytes32[] memory key, + bytes32 schema, + uint8 schemaIndex, + uint256 newLengthAtIndex + ) internal { + // Load dynamic data length from storage + bytes32 dynamicSchemaLengthSlot = _getDynamicDataLengthLocation(table, key); + bytes32 encodedLengths = Storage.read(dynamicSchemaLengthSlot); + + // Get current lengths (total and at index) + uint256 totalLength = _decodeDynamicDataTotalLength(encodedLengths); + uint256 currentLengthAtIndex = _decodeDynamicDataLengthAtIndex(encodedLengths, schema, schemaIndex); + + // Compute the difference and update the total length + int256 lengthDiff = int256(newLengthAtIndex) - int256(currentLengthAtIndex); + totalLength = uint256(int256(totalLength) + lengthDiff); + + // Compute dynamic schema index and offset into encoded lengths + uint8 dynamicSchemaIndex = schemaIndex - _getNumStaticFields(schema); + uint256 offset = 4 + dynamicSchemaIndex * 2; // (4 bytes total length, 2 bytes per dynamic schema) + + // Encode the new lengths + encodedLengths = Bytes.setBytes4(encodedLengths, 0, bytes4(uint32(totalLength))); + + encodedLengths = Bytes.setBytes2(encodedLengths, offset, bytes2(uint16(newLengthAtIndex))); + + // Set the new lengths + Storage.write(dynamicSchemaLengthSlot, encodedLengths); } } diff --git a/packages/solecs/v2/Types.sol b/packages/solecs/v2/Types.sol index e616b6c4fa..a5e59d75cc 100644 --- a/packages/solecs/v2/Types.sol +++ b/packages/solecs/v2/Types.sol @@ -38,6 +38,14 @@ function getStaticByteLength(SchemaType schemaType) pure returns (uint256) { } } +function getElementByteLength(SchemaType schemaType) pure returns (uint256) { + if (schemaType == SchemaType.Uint32Array) { + return 4; + } else { + return getStaticByteLength(schemaType); + } +} + /** * Returns true if the schema type has a fixed length * (Because Solidity doesn't support constant arrays, we need to use a function) diff --git a/packages/solecs/v2/test/Bytes.t.sol b/packages/solecs/v2/test/Bytes.t.sol index acc34daa34..51362be20c 100644 --- a/packages/solecs/v2/test/Bytes.t.sol +++ b/packages/solecs/v2/test/Bytes.t.sol @@ -300,4 +300,49 @@ contract BytesTest is DSTestPlus { bytes memory input = bytes.concat(lengths, Bytes.from(input1), Bytes.from(input2)); } + + function testSetBytes1() public { + bytes32 input = bytes32(0); + + uint256 gas = gasleft(); + Bytes.setBytes1(input, 8, 0xff); + gas = gas - gasleft(); + console.log("gas used: %s", gas); + + assertEq(Bytes.setBytes1(input, 0, 0x01), bytes32(bytes1(0x01))); + assertEq(Bytes.setBytes1(input, 31, 0x01), bytes32(uint256(0x01))); + } + + function testSetBytes2() public { + bytes32 input = bytes32(0); + + uint256 gas = gasleft(); + Bytes.setBytes2(input, 8, 0xffff); + gas = gas - gasleft(); + console.log("gas used: %s", gas); + + assertEq(Bytes.setBytes2(input, 0, 0xffff), bytes32(bytes2(0xffff))); + assertEq(Bytes.setBytes2(input, 30, 0xffff), bytes32(uint256(0xffff))); + } + + function testSetBytes4() public { + bytes32 input = bytes32(0); + + uint256 gas = gasleft(); + Bytes.setBytes4(input, 8, 0xffffffff); + gas = gas - gasleft(); + console.log("gas used: %s", gas); + + assertEq(Bytes.setBytes4(input, 0, 0xffffffff), bytes32(bytes4(0xffffffff))); + assertEq(Bytes.setBytes4(input, 30, 0xffffffff), bytes32(uint256(0xffff))); + assertEq(Bytes.setBytes4(input, 28, 0xffffffff), bytes32(uint256(0xffffffff))); + + bytes32 input2 = bytes32(0x0000000a000a0000000000000000000000000000000000000000000000000000); + bytes4 overwrite = bytes4(0x0000006d); + + assertEq( + Bytes.setBytes4(input2, 0, overwrite), + bytes32(0x0000006d000a0000000000000000000000000000000000000000000000000000) + ); + } } diff --git a/packages/solecs/v2/test/StoreCore.t.sol b/packages/solecs/v2/test/StoreCore.t.sol index b16dff3a0d..51166aa135 100644 --- a/packages/solecs/v2/test/StoreCore.t.sol +++ b/packages/solecs/v2/test/StoreCore.t.sol @@ -85,6 +85,61 @@ contract StoreCoreTest is DSTestPlus { assertEq(uint8(Bytes.slice1(loadedSchema, 5)), uint8(SchemaType.Uint16)); } + function testSetAndGetDynamicDataLength() public { + bytes32 table = keccak256("some.table"); + + SchemaType[] memory _schema = new SchemaType[](6); + _schema[0] = SchemaType.Uint8; // 1 byte + _schema[1] = SchemaType.Uint16; // 2 bytes + _schema[2] = SchemaType.Uint32; // 4 bytes + _schema[3] = SchemaType.Uint32Array; // 0 bytes (because it's dynamic) + _schema[4] = SchemaType.Uint32Array; // 0 bytes (because it's dynamic) + + bytes32 schema = StoreCore.encodeSchema(_schema); + + // Register schema + StoreCore.registerSchema(table, schema); + + // Create some key + bytes32[] memory key = new bytes32[](1); + key[0] = bytes32("some key"); + + // Set dynamic data length of index 3 + uint256 gas = gasleft(); + StoreCore._setDynamicDataLengthAtIndex(table, key, schema, 3, 10); + gas = gas - gasleft(); + console.log("gas used (set length): %s", gas); + + assertEq(StoreCore._getDynamicDataLengthAtIndex(table, key, schema, 3), 10); + assertEq(StoreCore._getDynamicDataLengthAtIndex(table, key, schema, 4), 0); + assertEq(StoreCore._getDynamicDataTotalLength(table, key), 10); + + // Set dynamic data length of index 4 + gas = gasleft(); + StoreCore._setDynamicDataLengthAtIndex(table, key, schema, 4, 99); + gas = gas - gasleft(); + console.log("gas used (set length): %s", gas); + + assertEq(StoreCore._getDynamicDataLengthAtIndex(table, key, schema, 3), 10); + assertEq(StoreCore._getDynamicDataLengthAtIndex(table, key, schema, 4), 99); + assertEq(StoreCore._getDynamicDataTotalLength(table, key), 109); + + // Reduce dynamic data length of index 3 again + gas = gasleft(); + StoreCore._setDynamicDataLengthAtIndex(table, key, schema, 3, 5); + gas = gas - gasleft(); + console.log("gas used (set length): %s", gas); + + assertEq(StoreCore._getDynamicDataLengthAtIndex(table, key, schema, 3), 5); + assertEq(StoreCore._getDynamicDataLengthAtIndex(table, key, schema, 4), 99); + assertEq(StoreCore._getDynamicDataTotalLength(table, key), 104); + + gas = gasleft(); + StoreCore._getDynamicDataLengthAtIndex(table, key, schema, 3); + gas = gas - gasleft(); + console.log("gas used (get length at index): %s", gas); + } + function testSetAndGetData() public { // Register table's schema bytes32 schema = bytes32( @@ -180,10 +235,4 @@ contract StoreCoreTest is DSTestPlus { assertTrue(Bytes.equals(data, loadedData)); } - - function testSetFieldWithOffset() public { - // Should not override the data before the offset - // Should not override the data after the field - // revert("todo"); - } } From ad6b13247395ab0c11509647381bad2b9bd18a55 Mon Sep 17 00:00:00 2001 From: alvrs Date: Thu, 26 Jan 2023 15:03:26 +0100 Subject: [PATCH 18/82] feat(solecs): wip data model - add logic and tests to read and write dynamic data --- packages/solecs/v2/Bytes.sol | 11 +- packages/solecs/v2/IStore.sol | 36 +--- packages/solecs/v2/Memory.sol | 33 +++ packages/solecs/v2/Storage.sol | 53 ++++- packages/solecs/v2/StoreCore.sol | 225 +++++++++++++++------ packages/solecs/v2/StoreSwitch.sol | 57 +----- packages/solecs/v2/StoreView.sol | 58 +++--- packages/solecs/v2/World.sol | 11 +- packages/solecs/v2/tables/RouteTable.sol | 2 +- packages/solecs/v2/tables/Vector2Table.sol | 2 +- packages/solecs/v2/test/Bytes.t.sol | 19 ++ packages/solecs/v2/test/RouteTable.t.sol | 2 +- packages/solecs/v2/test/StoreCore.t.sol | 153 ++++++++++++-- 13 files changed, 456 insertions(+), 206 deletions(-) create mode 100644 packages/solecs/v2/Memory.sol diff --git a/packages/solecs/v2/Bytes.sol b/packages/solecs/v2/Bytes.sol index a6828fd2ff..5e5a99e2b5 100644 --- a/packages/solecs/v2/Bytes.sol +++ b/packages/solecs/v2/Bytes.sol @@ -60,6 +60,15 @@ library Bytes { return _from(ptr, 2); } + function from(uint32[] memory input) internal pure returns (bytes memory output) { + bytes32 ptr; + assembly { + ptr := input + } + + return _from(ptr, 4); + } + function _from(bytes32 _ptr, uint256 _bytesPerElement) internal pure returns (bytes memory output) { assembly { let inputLength := mload(_ptr) @@ -235,7 +244,7 @@ library Bytes { bytes32 input, uint256 index, bytes4 overwrite - ) internal view returns (bytes32 output) { + ) internal pure returns (bytes32 output) { bytes4 mask = 0xffffffff; assembly { mask := shr(mul(8, index), mask) // create a mask by shifting 0xffffffff right by index bytes diff --git a/packages/solecs/v2/IStore.sol b/packages/solecs/v2/IStore.sol index 764eba241c..16fcff3fee 100644 --- a/packages/solecs/v2/IStore.sol +++ b/packages/solecs/v2/IStore.sol @@ -11,34 +11,25 @@ interface IStore { function getSchema(bytes32 table) external view returns (bytes32 schema); - // Set full record (including full array) + // Set full record (including full dynamic data) function set( bytes32 table, bytes32[] memory key, + bytes32 encodedDynamicLength, bytes memory data ) external; - // Set partial data at schema index - function setField( - bytes32 table, - bytes32[] memory key, - uint8 schemaIndex, - bytes memory data - ) external; - - // Set full record of a single item at a given array index - function setArrayIndex( + // Set full record (including full array) + function setStaticData( bytes32 table, bytes32[] memory key, - uint16 arrayIndex, bytes memory data ) external; - // Set partial data of a single item at a given array index - function setArrayIndexField( + // Set partial data at schema index + function setField( bytes32 table, bytes32[] memory key, - uint16 arrayIndex, uint8 schemaIndex, bytes memory data ) external; @@ -53,21 +44,6 @@ interface IStore { uint8 schemaIndex ) external view returns (bytes memory data); - // Get full record of a single item at a given array index - function getArrayIndex( - bytes32 table, - bytes32[] memory key, - uint16 arrayIndex - ) external view returns (bytes memory data); - - // Get partial data of a single item at a given array index - function getArrayIndexField( - bytes32 table, - bytes32[] memory key, - uint16 arrayIndex, - uint8 schemaIndex - ) external view returns (bytes memory data); - // If this function exists on the contract, it is a store // TODO: benchmark this vs. using a known storage slot to determine whether a contract is a Store function isStore() external view; diff --git a/packages/solecs/v2/Memory.sol b/packages/solecs/v2/Memory.sol new file mode 100644 index 0000000000..7b81477458 --- /dev/null +++ b/packages/solecs/v2/Memory.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import { Utils } from "./Utils.sol"; +import { Bytes } from "./Bytes.sol"; + +library Memory { + function read(uint256 memoryPointer) internal pure returns (bytes32 data) { + return read(bytes32(memoryPointer)); + } + + function read(bytes32 memoryPointer) internal pure returns (bytes32 data) { + assembly { + data := mload(memoryPointer) + } + } + + function read(uint256 memoryPointer, uint256 offset) internal pure returns (bytes32 data) { + return read(bytes32(memoryPointer), offset); + } + + function read(bytes32 memoryPointer, uint256 offset) internal pure returns (bytes32 data) { + assembly { + data := mload(add(memoryPointer, offset)) + } + } + + function ptr(bytes memory data) internal pure returns (uint256 memoryPointer) { + assembly { + memoryPointer := add(data, 0x20) + } + } +} diff --git a/packages/solecs/v2/Storage.sol b/packages/solecs/v2/Storage.sol index a8bd9e26fb..e65c90b663 100644 --- a/packages/solecs/v2/Storage.sol +++ b/packages/solecs/v2/Storage.sol @@ -3,6 +3,7 @@ pragma solidity >=0.8.0; import { Utils } from "./Utils.sol"; import { Bytes } from "./Bytes.sol"; +import { Memory } from "./Memory.sol"; import "./Buffer.sol"; library Storage { @@ -30,6 +31,27 @@ library Storage { write(uint256(storagePointer), offset, data); } + function write( + uint256 storagePointer, + uint256 offset, + bytes memory data + ) internal { + uint256 memoryPointer; + assembly { + memoryPointer := add(data, 0x20) + } + write(storagePointer, offset, memoryPointer, data.length); + } + + function write( + bytes32 storagePointer, + uint256 offset, + uint256 memoryPointer, + uint256 length + ) internal { + write(uint256(storagePointer), offset, memoryPointer, length); + } + /** * @dev Write raw bytes to storage at the given storagePointer and offset (keeping the rest of the word intact) * TODO: this implementation is optimized for readability, but not very gas efficient. We should optimize this using assembly once we've settled on a spec. @@ -37,34 +59,35 @@ library Storage { function write( uint256 storagePointer, uint256 offset, - bytes memory data + uint256 memoryPointer, + uint256 length ) internal { - uint256 numWords = Utils.divCeil(data.length + offset, 32); + uint256 numWords = Utils.divCeil(length + offset, 32); uint256 bytesWritten; for (uint256 i; i < numWords; i++) { // If this is the first word, and there is an offset, apply a mask to beginning if ((i == 0 && offset > 0)) { - uint256 _length = data.length > 32 ? 32 - offset : data.length; // // the number of bytes to write + uint256 _length = length > 32 ? 32 - offset : length; // // the number of bytes to write _writePartialWord( storagePointer, // the word to update offset, // the offset in bytes to start writing _length, - bytes32(data) // Pass the first 32 bytes of the data + Memory.read(memoryPointer) // Pass the first 32 bytes of the data ); bytesWritten += _length; // If this is the last word, and there is a partial word, apply a mask to the end - } else if (i == numWords - 1 && (data.length + offset) % 32 > 0) { + } else if (i == numWords - 1 && (length + offset) % 32 > 0) { _writePartialWord( storagePointer + i, // the word to update 0, // the offset in bytes to start writing - (data.length + offset) % 32, // the number of bytes to write - Bytes.slice32(data, bytesWritten) // the data to write + (length + offset) % 32, // the number of bytes to write + Memory.read(memoryPointer, bytesWritten) // the data to write ); // Else, just write the word } else { - _writeWord(storagePointer + i, Bytes.slice32(data, bytesWritten)); + _writeWord(storagePointer + i, Memory.read(memoryPointer, bytesWritten)); bytesWritten += 32; } } @@ -103,7 +126,19 @@ library Storage { uint256 length ) internal view returns (bytes memory) { Buffer buf = Buffer_.allocate(uint128(length)); + read(storagePointer, offset, length, buf); + return buf.toBytes(); + } + /** + * @dev Append raw bytes from storage at the given storagePointer, offset, and length to the given buffer + */ + function read( + uint256 storagePointer, + uint256 offset, + uint256 length, + Buffer buf + ) internal view { uint256 numWords = Utils.divCeil(length + offset, 32); uint256 _length; @@ -137,8 +172,6 @@ library Storage { buf.appendUnchecked(_loadWord(storagePointer + i), 32); } } - - return buf.toBytes(); } /** diff --git a/packages/solecs/v2/StoreCore.sol b/packages/solecs/v2/StoreCore.sol index 6296d8411b..7154ddbc42 100644 --- a/packages/solecs/v2/StoreCore.sol +++ b/packages/solecs/v2/StoreCore.sol @@ -4,6 +4,7 @@ import { Utils } from "./Utils.sol"; import { Bytes } from "./Bytes.sol"; import { SchemaType, getStaticByteLength, getElementByteLength } from "./Types.sol"; import { Storage } from "./Storage.sol"; +import { Memory } from "./Memory.sol"; import { console } from "forge-std/console.sol"; import "./Buffer.sol"; @@ -60,19 +61,68 @@ library StoreCore { ************************************************************************/ /** - * Set full record for the given table and key tuple - * (Note: this will overwrite the entire record, including any array data) + * Set full data record for the given table and key tuple (static and dynamic data) */ function set( bytes32 table, bytes32[] memory key, + bytes32 encodedDynamicLength, bytes memory data ) internal { // verify the value has the correct length for the table (based on the table's schema) // to prevent invalid data from being stored bytes32 schema = getSchema(table); - if (_getStaticDataLength(schema) != data.length) + + // Verify static data length + dynamic data length matches the given data + uint256 staticLength = _getStaticDataLength(schema); + uint256 expectedLength = staticLength + _decodeDynamicDataTotalLength(encodedDynamicLength); + if (expectedLength != data.length) { + revert StoreCore_InvalidDataLength(expectedLength, data.length); + } + + // Store the static data at the static data location + bytes32 staticDataLocation = _getStaticDataLocation(table, key); + uint256 memoryPointer = Memory.ptr(data); + Storage.write(staticDataLocation, 0, memoryPointer, staticLength); + memoryPointer += staticLength; // move the memory pointer to the start of the dynamic data + + // Store the dynamic data length at the dynamic data length location + bytes32 dynamicDataLengthLocation = _getDynamicDataLengthLocation(table, key); + Storage.write(dynamicDataLengthLocation, encodedDynamicLength); + + // For every dynamic element, slice off the dynamic data and store it at the dynamic location + bytes32 dynamicDataLocation; + uint256 dynamicDataLength; + for (uint8 i; i < _getNumDynamicFields(schema); ) { + dynamicDataLocation = _getDynamicDataLocation(table, key, i); + dynamicDataLength = _decodeDynamicDataLengthAtIndex(encodedDynamicLength, i); + Storage.write(dynamicDataLocation, 0, memoryPointer, dynamicDataLength); + memoryPointer += dynamicDataLength; // move the memory pointer to the start of the next dynamic data + unchecked { + i++; + } + } + + // Emit event to notify indexers + emit StoreUpdate(table, key, 0, data); + } + + /** + * Set full static data record for the given table and key tuple (without any dynamic data) + */ + function setStaticData( + bytes32 table, + bytes32[] memory key, + bytes memory data + ) internal { + // verify the value has the correct length for the table (based on the table's schema) + // to prevent invalid data from being stored + bytes32 schema = getSchema(table); + + // Verify the static data length matches the table schema + if (_getStaticDataLength(schema) != data.length) { revert StoreCore_InvalidDataLength(_getStaticDataLength(schema), data.length); + } // Store the provided value in storage bytes32 location = _getStaticDataLocation(table, key); @@ -88,8 +138,22 @@ library StoreCore { uint8 schemaIndex, bytes memory data ) internal { - // verify the value has the correct length for the field bytes32 schema = getSchema(table); + if (schemaIndex < _getNumStaticFields(schema)) { + _setStaticField(table, key, schema, schemaIndex, data); + } else { + _setDynamicField(table, key, schema, schemaIndex, data); + } + } + + function _setStaticField( + bytes32 table, + bytes32[] memory key, + bytes32 schema, + uint8 schemaIndex, + bytes memory data + ) internal { + // verify the value has the correct length for the field SchemaType schemaType = _getSchemaTypeAtIndex(schema, schemaIndex); if (getStaticByteLength(schemaType) != data.length) revert StoreCore_InvalidDataLength(getStaticByteLength(schemaType), data.length); @@ -103,23 +167,24 @@ library StoreCore { emit StoreUpdate(table, key, schemaIndex, data); } - function setArrayIndex( - bytes32, - bytes32[] memory, - uint16, - bytes memory - ) internal pure { - revert StoreCore_NotImplemented(); - } + function _setDynamicField( + bytes32 table, + bytes32[] memory key, + bytes32 schema, + uint8 schemaIndex, + bytes memory data + ) internal { + uint8 dynamicSchemaIndex = schemaIndex - _getNumStaticFields(schema); - function setArrayIndexField( - bytes32, - bytes32[] memory, - uint16, - uint8, - bytes memory - ) internal pure { - revert StoreCore_NotImplemented(); + // Update the dynamic data length + _setDynamicDataLengthAtIndex(table, key, dynamicSchemaIndex, data.length); + + // Store the provided value in storage + bytes32 dynamicDataLocation = _getDynamicDataLocation(table, key, dynamicSchemaIndex); + Storage.write(dynamicDataLocation, data); + + // Emit event to notify indexers + emit StoreUpdate(table, key, schemaIndex, data); } /************************************************************************ @@ -129,7 +194,7 @@ library StoreCore { ************************************************************************/ /** - * Get full record for the given table and key tuple (compute length from schema) + * Get full static record for the given table and key tuple (loading schema from storage) */ function get(bytes32 table, bytes32[] memory key) internal view returns (bytes memory) { // Get schema for this table @@ -138,35 +203,57 @@ library StoreCore { return get(table, key, schema); } - function getField( - bytes32, - bytes32[] memory, - uint8 - ) internal pure returns (bytes memory) { - revert StoreCore_NotImplemented(); - } + /** + * Get full data (static and dynamic) for the given table and key tuple, with the given schema + */ + function get( + bytes32 table, + bytes32[] memory key, + bytes32 schema + ) internal view returns (bytes memory) { + // Get static data length + uint256 staticDataLength = _getStaticDataLength(schema); + + // Load the dynamic data length if there are dynamic fields + bytes32 encodedDynamicLength; + uint256 dynamicDataTotalLength; + uint256 numDynamicFields = _getNumDynamicFields(schema); + if (numDynamicFields > 0) { + encodedDynamicLength = _loadEncodedDynamicDataLength(table, key); + dynamicDataTotalLength = _decodeDynamicDataTotalLength(encodedDynamicLength); + } - function getArrayIndex( - bytes32, - bytes32[] memory, - uint16 - ) internal pure returns (bytes memory) { - revert StoreCore_NotImplemented(); + // Allocate a buffer for the full data (static and dynamic) + Buffer buffer = Buffer_.allocate(uint128(staticDataLength + dynamicDataTotalLength)); + + // Load the static data from storage and append it to the buffer + buffer.append(getStaticData(table, key, schema)); + + // Append dynamic data to the buffer + for (uint8 i; i < numDynamicFields; i++) { + uint256 dynamicDataLocation = uint256(_getDynamicDataLocation(table, key, i)); + uint256 dynamicDataLengthAtIndex = _decodeDynamicDataLengthAtIndex(encodedDynamicLength, i); + Storage.read(dynamicDataLocation, 0, dynamicDataLengthAtIndex, buffer); + } + + // Return the buffer as bytes + return buffer.toBytes(); } - function getArrayIndexField( - bytes32, - bytes32[] memory, - uint16, - uint8 - ) internal pure returns (bytes memory) { - revert StoreCore_NotImplemented(); + /** + * Get full static record for the given table and key tuple (loading schema from storage) + */ + function getStaticData(bytes32 table, bytes32[] memory key) internal view returns (bytes memory) { + // Get schema for this table + bytes32 schema = getSchema(table); + + return getStaticData(table, key, schema); } /** - * Get full data for the given table and key tuple, with the given length + * Get full static data for the given table and key tuple, with the given schema */ - function get( + function getStaticData( bytes32 table, bytes32[] memory key, bytes32 schema @@ -176,6 +263,15 @@ library StoreCore { return Storage.read(location, _getStaticDataLength(schema)); } + // TODO + function getField( + bytes32, + bytes32[] memory, + uint8 + ) internal pure returns (bytes memory) { + revert StoreCore_NotImplemented(); + } + /************************************************************************ * * HELPER FUNCTIONS @@ -189,11 +285,10 @@ library StoreCore { * - 1 byte for number of dynamic size fields * - 28 bytes for 28 schema types */ - function encodeSchema(SchemaType[] memory _schema) internal pure returns (bytes32) { + function encodeSchema(SchemaType[] memory _schema) internal pure returns (bytes32 schema) { if (_schema.length > 28) revert StoreCore_SchemaTooLong(); uint16 length; uint8 staticFields; - bytes32 schema = bytes32(0); // Compute the length of the schema and the number of static fields // and store the schema types in the encoded schema @@ -212,8 +307,23 @@ library StoreCore { schema = Bytes.setBytes1(schema, 1, bytes1(uint8(length))); // lower length byte schema = Bytes.setBytes1(schema, 2, bytes1(staticFields)); // number of static fields schema = Bytes.setBytes1(schema, 3, bytes1(uint8(_schema.length) - staticFields)); // number of dynamic fields + } + + function encodeDynamicDataLength(uint16[] memory lengths) internal pure returns (bytes32 encodedLength) { + uint32 totalLength; + + // Compute the total length of the dynamic data + // and store the encoded lengths of each item + for (uint256 i; i < lengths.length; ) { + encodedLength = Bytes.setBytes2(encodedLength, 4 + 2 * i, bytes2(lengths[i])); + totalLength += lengths[i]; + unchecked { + i++; + } + } - return schema; + // Store total length + encodedLength = Bytes.setBytes4(encodedLength, 0, bytes4(totalLength)); } /************************************************************************ @@ -320,11 +430,9 @@ library StoreCore { */ function _decodeDynamicDataLengthAtIndex( bytes32 encodedLengths, - bytes32 schema, - uint8 schemaIndex + uint8 dynamicSchemaIndex // schemaIndex - numStaticFields ) internal pure returns (uint256) { // Compute dynamic schema index and offset into encoded lengths - uint8 dynamicSchemaIndex = schemaIndex - _getNumStaticFields(schema); uint256 offset = 4 + dynamicSchemaIndex * 2; // (4 bytes total length, 2 bytes per dynamic schema) // Return dynamic data length in bytes for the given index @@ -350,14 +458,13 @@ library StoreCore { function _getDynamicDataLengthAtIndex( bytes32 table, bytes32[] memory key, - bytes32 schema, - uint8 schemaIndex + uint8 dynamicSchemaIndex // schemaIndex - numStaticFields ) internal view returns (uint256) { // Load dynamic data length from storage bytes32 encodedLengths = _loadEncodedDynamicDataLength(table, key); // Return dynamic data length in bytes for the given index - return _decodeDynamicDataLengthAtIndex(encodedLengths, schema, schemaIndex); + return _decodeDynamicDataLengthAtIndex(encodedLengths, dynamicSchemaIndex); } /** @@ -366,8 +473,7 @@ library StoreCore { function _setDynamicDataLengthAtIndex( bytes32 table, bytes32[] memory key, - bytes32 schema, - uint8 schemaIndex, + uint8 dynamicSchemaIndex, // schemaIndex - numStaticFields uint256 newLengthAtIndex ) internal { // Load dynamic data length from storage @@ -376,14 +482,13 @@ library StoreCore { // Get current lengths (total and at index) uint256 totalLength = _decodeDynamicDataTotalLength(encodedLengths); - uint256 currentLengthAtIndex = _decodeDynamicDataLengthAtIndex(encodedLengths, schema, schemaIndex); + uint256 currentLengthAtIndex = _decodeDynamicDataLengthAtIndex(encodedLengths, dynamicSchemaIndex); // Compute the difference and update the total length int256 lengthDiff = int256(newLengthAtIndex) - int256(currentLengthAtIndex); totalLength = uint256(int256(totalLength) + lengthDiff); // Compute dynamic schema index and offset into encoded lengths - uint8 dynamicSchemaIndex = schemaIndex - _getNumStaticFields(schema); uint256 offset = 4 + dynamicSchemaIndex * 2; // (4 bytes total length, 2 bytes per dynamic schema) // Encode the new lengths @@ -404,17 +509,17 @@ library StoreCoreExt { * ************************************************************************/ - function set( + function setStaticData( bytes32 table, bytes32 _key, bytes memory data ) internal { bytes32[] memory key = new bytes32[](1); key[0] = _key; - StoreCore.set(table, key, data); + StoreCore.setStaticData(table, key, data); } - function set( + function setStaticData( bytes32 table, bytes32[2] memory _key, bytes memory data @@ -422,7 +527,7 @@ library StoreCoreExt { bytes32[] memory key = new bytes32[](2); key[0] = _key[0]; key[1] = _key[1]; - StoreCore.set(table, key, data); + StoreCore.setStaticData(table, key, data); } /************************************************************************ diff --git a/packages/solecs/v2/StoreSwitch.sol b/packages/solecs/v2/StoreSwitch.sol index e63946fbe0..bde158bf17 100644 --- a/packages/solecs/v2/StoreSwitch.sol +++ b/packages/solecs/v2/StoreSwitch.sol @@ -40,52 +40,38 @@ library StoreSwitch { function set( bytes32 table, bytes32[] memory key, + bytes32 encodedDynamicLength, bytes memory data ) internal { if (isDelegateCall()) { - StoreCore.set(table, key, data); + StoreCore.set(table, key, encodedDynamicLength, data); } else { - IStore(msg.sender).set(table, key, data); + IStore(msg.sender).set(table, key, encodedDynamicLength, data); } } - function setField( + function setStaticData( bytes32 table, bytes32[] memory key, - uint8 fieldIndex, bytes memory data ) internal { if (isDelegateCall()) { - StoreCore.setField(table, key, fieldIndex, data); + StoreCore.setStaticData(table, key, data); } else { - IStore(msg.sender).setField(table, key, fieldIndex, data); + IStore(msg.sender).setStaticData(table, key, data); } } - function setArrayIndex( - bytes32 table, - bytes32[] memory key, - uint16 arrayIndex, - bytes memory data - ) internal { - if (isDelegateCall()) { - StoreCore.setArrayIndex(table, key, arrayIndex, data); - } else { - IStore(msg.sender).setArrayIndex(table, key, arrayIndex, data); - } - } - - function setArrayIndexField( + function setField( bytes32 table, bytes32[] memory key, - uint16 arrayIndex, uint8 fieldIndex, bytes memory data ) internal { if (isDelegateCall()) { - StoreCore.setArrayIndexField(table, key, arrayIndex, fieldIndex, data); + StoreCore.setField(table, key, fieldIndex, data); } else { - IStore(msg.sender).setArrayIndexField(table, key, arrayIndex, fieldIndex, data); + IStore(msg.sender).setField(table, key, fieldIndex, data); } } @@ -108,29 +94,4 @@ library StoreSwitch { return IStore(msg.sender).getField(table, key, fieldIndex); } } - - function getArrayIndex( - bytes32 table, - bytes32[] memory key, - uint16 arrayIndex - ) internal view returns (bytes memory) { - if (isDelegateCall()) { - return StoreCore.getArrayIndex(table, key, arrayIndex); - } else { - return IStore(msg.sender).getArrayIndex(table, key, arrayIndex); - } - } - - function getArrayIndexField( - bytes32 table, - bytes32[] memory key, - uint16 arrayIndex, - uint8 fieldIndex - ) internal view returns (bytes memory) { - if (isDelegateCall()) { - return StoreCore.getArrayIndexField(table, key, arrayIndex, fieldIndex); - } else { - return IStore(msg.sender).getArrayIndexField(table, key, arrayIndex, fieldIndex); - } - } } diff --git a/packages/solecs/v2/StoreView.sol b/packages/solecs/v2/StoreView.sol index 971358c800..607ced1098 100644 --- a/packages/solecs/v2/StoreView.sol +++ b/packages/solecs/v2/StoreView.sol @@ -18,6 +18,15 @@ contract StoreView is IStore { } function set( + bytes32, + bytes32[] memory, + bytes32, + bytes memory + ) public virtual { + revert Store_BaseContractNotImplemented(); + } + + function setStaticData( bytes32, bytes32[] memory, bytes memory @@ -27,31 +36,31 @@ contract StoreView is IStore { // Set partial data at schema index function setField( - bytes32 table, - bytes32[] memory key, - uint8 schemaIndex, - bytes memory data + bytes32, + bytes32[] memory, + uint8, + bytes memory ) public virtual { revert Store_BaseContractNotImplemented(); } // Set full record of a single item at a given array index function setArrayIndex( - bytes32 table, - bytes32[] memory key, - uint16 arrayIndex, - bytes memory data + bytes32, + bytes32[] memory, + uint16, + bytes memory ) public virtual { revert Store_BaseContractNotImplemented(); } // Set partial data of a single item at a given array index function setArrayIndexField( - bytes32 table, - bytes32[] memory key, - uint16 arrayIndex, - uint8 schemaIndex, - bytes memory data + bytes32, + bytes32[] memory, + uint16, + uint8, + bytes memory ) public virtual { revert Store_BaseContractNotImplemented(); } @@ -66,27 +75,8 @@ contract StoreView is IStore { bytes32 table, bytes32[] memory key, uint8 schemaIndex - ) public view returns (bytes memory data) { - revert Store_BaseContractNotImplemented(); - } - - // Get full record of a single item at a given array index - function getArrayIndex( - bytes32 table, - bytes32[] memory key, - uint16 arrayIndex - ) public view returns (bytes memory data) { - revert Store_BaseContractNotImplemented(); - } - - // Get partial data of a single item at a given array index - function getArrayIndexField( - bytes32 table, - bytes32[] memory key, - uint16 arrayIndex, - uint8 schemaIndex - ) public view returns (bytes memory data) { - revert Store_BaseContractNotImplemented(); + ) public pure returns (bytes memory data) { + data = StoreCore.getField(table, key, schemaIndex); } function isStore() public view {} diff --git a/packages/solecs/v2/World.sol b/packages/solecs/v2/World.sol index 4ece390954..4d23b33d5e 100644 --- a/packages/solecs/v2/World.sol +++ b/packages/solecs/v2/World.sol @@ -25,9 +25,18 @@ contract World is StoreView { function set( bytes32 table, bytes32[] memory key, + bytes32 encodedDynamicLength, bytes memory data ) public override { - StoreCore.set(table, key, data); + StoreCore.set(table, key, encodedDynamicLength, data); + } + + function setStaticData( + bytes32 table, + bytes32[] memory key, + bytes memory data + ) public override { + StoreCore.setStaticData(table, key, data); } function setField( diff --git a/packages/solecs/v2/tables/RouteTable.sol b/packages/solecs/v2/tables/RouteTable.sol index 00b7073b0d..79f1979808 100644 --- a/packages/solecs/v2/tables/RouteTable.sol +++ b/packages/solecs/v2/tables/RouteTable.sol @@ -54,7 +54,7 @@ library RouteTable { bytes memory data = bytes.concat(bytes20(addr), bytes4(selector), bytes1(executionMode)); bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = key; - StoreSwitch.set(id, keyTuple, data); + StoreSwitch.setStaticData(id, keyTuple, data); } function set(bytes32 key, Route memory data) internal { diff --git a/packages/solecs/v2/tables/Vector2Table.sol b/packages/solecs/v2/tables/Vector2Table.sol index b7817fbdbb..641a6925ff 100644 --- a/packages/solecs/v2/tables/Vector2Table.sol +++ b/packages/solecs/v2/tables/Vector2Table.sol @@ -47,7 +47,7 @@ library Vector2Table { bytes memory data = bytes.concat(bytes4(x), bytes4(y)); bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = key; - StoreSwitch.set(id, keyTuple, data); + StoreSwitch.setStaticData(id, keyTuple, data); } function set(bytes32 key, Vector2 memory vec2) internal { diff --git a/packages/solecs/v2/test/Bytes.t.sol b/packages/solecs/v2/test/Bytes.t.sol index 51362be20c..547c683bbc 100644 --- a/packages/solecs/v2/test/Bytes.t.sol +++ b/packages/solecs/v2/test/Bytes.t.sol @@ -54,6 +54,25 @@ contract BytesTest is DSTestPlus { assertEq(uint256(uint8(output[5])), 0x06); } + function testFromUint32Array() public { + uint32[] memory input = new uint32[](2); + input[0] = 0x01020304; + input[1] = 0x05060708; + uint256 gas = gasleft(); + bytes memory output = Bytes.from(input); + gas = gas - gasleft(); + console.log("gas used: %s", gas); + assertEq(output.length, 8); + assertEq(uint256(uint8(output[0])), 0x01); + assertEq(uint256(uint8(output[1])), 0x02); + assertEq(uint256(uint8(output[2])), 0x03); + assertEq(uint256(uint8(output[3])), 0x04); + assertEq(uint256(uint8(output[4])), 0x05); + assertEq(uint256(uint8(output[5])), 0x06); + assertEq(uint256(uint8(output[6])), 0x07); + assertEq(uint256(uint8(output[7])), 0x08); + } + function testToBytes32() public { bytes memory input = new bytes(32); input[0] = 0x01; diff --git a/packages/solecs/v2/test/RouteTable.t.sol b/packages/solecs/v2/test/RouteTable.t.sol index 678fde4d4a..d708457a58 100644 --- a/packages/solecs/v2/test/RouteTable.t.sol +++ b/packages/solecs/v2/test/RouteTable.t.sol @@ -8,7 +8,7 @@ import { StoreCore } from "../StoreCore.sol"; import { SchemaType } from "../Types.sol"; import { StoreView } from "../StoreView.sol"; -contract SystemTableTest is DSTestPlus, StoreView { +contract RouteTableTest is DSTestPlus, StoreView { function testRegisterAndGetSchema() public { uint256 gas = gasleft(); RouteTable.registerSchema(); diff --git a/packages/solecs/v2/test/StoreCore.t.sol b/packages/solecs/v2/test/StoreCore.t.sol index 51166aa135..ca15b947dd 100644 --- a/packages/solecs/v2/test/StoreCore.t.sol +++ b/packages/solecs/v2/test/StoreCore.t.sol @@ -9,6 +9,8 @@ import { Bytes } from "../Bytes.sol"; import { SchemaType } from "../Types.sol"; contract StoreCoreTest is DSTestPlus { + TestStruct testStruct; + function testGetStaticDataLength() public { bytes32 schema = bytes32( bytes.concat(bytes2(uint16(3)), bytes1(uint8(SchemaType.Uint8)), bytes1(uint8(SchemaType.Uint16))) @@ -85,6 +87,35 @@ contract StoreCoreTest is DSTestPlus { assertEq(uint8(Bytes.slice1(loadedSchema, 5)), uint8(SchemaType.Uint16)); } + function testEncodeAndDecodeDynamicLength() public { + uint16[] memory lengths = new uint16[](4); + lengths[0] = 1; + lengths[1] = 2; + lengths[2] = 3; + lengths[3] = 4; + + uint256 gas = gasleft(); + bytes32 encodedLengths = StoreCore.encodeDynamicDataLength(lengths); + gas = gas - gasleft(); + console.log("gas used (encode): %s", gas); + + gas = gasleft(); + StoreCore._decodeDynamicDataLengthAtIndex(encodedLengths, 3); + gas = gas - gasleft(); + console.log("gas used (decode index): %s", gas); + + gas = gasleft(); + StoreCore._decodeDynamicDataTotalLength(encodedLengths); + gas = gas - gasleft(); + console.log("gas used (decode total): %s", gas); + + assertEq(StoreCore._decodeDynamicDataLengthAtIndex(encodedLengths, 0), 1); + assertEq(StoreCore._decodeDynamicDataLengthAtIndex(encodedLengths, 1), 2); + assertEq(StoreCore._decodeDynamicDataLengthAtIndex(encodedLengths, 2), 3); + assertEq(StoreCore._decodeDynamicDataLengthAtIndex(encodedLengths, 3), 4); + assertEq(StoreCore._decodeDynamicDataTotalLength(encodedLengths), 10); + } + function testSetAndGetDynamicDataLength() public { bytes32 table = keccak256("some.table"); @@ -104,43 +135,43 @@ contract StoreCoreTest is DSTestPlus { bytes32[] memory key = new bytes32[](1); key[0] = bytes32("some key"); - // Set dynamic data length of index 3 + // Set dynamic data length of dynamic index 0 uint256 gas = gasleft(); - StoreCore._setDynamicDataLengthAtIndex(table, key, schema, 3, 10); + StoreCore._setDynamicDataLengthAtIndex(table, key, 0, 10); gas = gas - gasleft(); console.log("gas used (set length): %s", gas); - assertEq(StoreCore._getDynamicDataLengthAtIndex(table, key, schema, 3), 10); - assertEq(StoreCore._getDynamicDataLengthAtIndex(table, key, schema, 4), 0); + assertEq(StoreCore._getDynamicDataLengthAtIndex(table, key, 0), 10); + assertEq(StoreCore._getDynamicDataLengthAtIndex(table, key, 1), 0); assertEq(StoreCore._getDynamicDataTotalLength(table, key), 10); - // Set dynamic data length of index 4 + // Set dynamic data length of dynamic index 1 gas = gasleft(); - StoreCore._setDynamicDataLengthAtIndex(table, key, schema, 4, 99); + StoreCore._setDynamicDataLengthAtIndex(table, key, 1, 99); gas = gas - gasleft(); console.log("gas used (set length): %s", gas); - assertEq(StoreCore._getDynamicDataLengthAtIndex(table, key, schema, 3), 10); - assertEq(StoreCore._getDynamicDataLengthAtIndex(table, key, schema, 4), 99); + assertEq(StoreCore._getDynamicDataLengthAtIndex(table, key, 0), 10); + assertEq(StoreCore._getDynamicDataLengthAtIndex(table, key, 1), 99); assertEq(StoreCore._getDynamicDataTotalLength(table, key), 109); - // Reduce dynamic data length of index 3 again + // Reduce dynamic data length of dynamic index 0 again gas = gasleft(); - StoreCore._setDynamicDataLengthAtIndex(table, key, schema, 3, 5); + StoreCore._setDynamicDataLengthAtIndex(table, key, 0, 5); gas = gas - gasleft(); console.log("gas used (set length): %s", gas); - assertEq(StoreCore._getDynamicDataLengthAtIndex(table, key, schema, 3), 5); - assertEq(StoreCore._getDynamicDataLengthAtIndex(table, key, schema, 4), 99); + assertEq(StoreCore._getDynamicDataLengthAtIndex(table, key, 0), 5); + assertEq(StoreCore._getDynamicDataLengthAtIndex(table, key, 1), 99); assertEq(StoreCore._getDynamicDataTotalLength(table, key), 104); gas = gasleft(); - StoreCore._getDynamicDataLengthAtIndex(table, key, schema, 3); + StoreCore._getDynamicDataLengthAtIndex(table, key, 0); gas = gas - gasleft(); console.log("gas used (get length at index): %s", gas); } - function testSetAndGetData() public { + function testSetAndGetStaticData() public { // Register table's schema bytes32 schema = bytes32( bytes.concat( @@ -162,7 +193,7 @@ contract StoreCoreTest is DSTestPlus { key[0] = keccak256("some.key"); uint256 gas = gasleft(); - StoreCore.set(table, key, data); + StoreCore.setStaticData(table, key, data); gas = gas - gasleft(); console.log("gas used (set): %s", gas); @@ -175,7 +206,7 @@ contract StoreCoreTest is DSTestPlus { assertTrue(Bytes.equals(data, loadedData)); } - function testFailSetAndGetData() public { + function testFailSetAndGetStaticData() public { // Register table's schema bytes32 schema = bytes32( bytes.concat( @@ -197,12 +228,13 @@ contract StoreCoreTest is DSTestPlus { key[0] = keccak256("some.key"); uint256 gas = gasleft(); - StoreCore.set(table, key, data); + // This should fail because the data is not 6 bytes long + StoreCore.setStaticData(table, key, data); gas = gas - gasleft(); console.log("gas used (set): %s", gas); } - function testSetAndGetDataSpanningWords() public { + function testSetAndGetStaticDataSpanningWords() public { // Register table's schema SchemaType[] memory schemaTypes = new SchemaType[](2); schemaTypes[0] = SchemaType.Uint128; @@ -223,7 +255,7 @@ contract StoreCoreTest is DSTestPlus { key[0] = keccak256("some.key"); uint256 gas = gasleft(); - StoreCore.set(table, key, data); + StoreCore.setStaticData(table, key, data); gas = gas - gasleft(); console.log("gas used (set): %s", gas); @@ -235,4 +267,87 @@ contract StoreCoreTest is DSTestPlus { assertTrue(Bytes.equals(data, loadedData)); } + + function testSetAndGetDynamicData() public { + bytes32 table = keccak256("some.table"); + + { + // Register table's schema + SchemaType[] memory schemaTypes = new SchemaType[](3); + schemaTypes[0] = SchemaType.Uint128; + schemaTypes[1] = SchemaType.Uint32Array; + schemaTypes[2] = SchemaType.Uint32Array; + bytes32 schema = StoreCore.encodeSchema(schemaTypes); + StoreCore.registerSchema(table, schema); + } + + bytes16 firstDataBytes = bytes16(0x0102030405060708090a0b0c0d0e0f10); + + bytes memory secondDataBytes; + { + uint32[] memory secondData = new uint32[](2); + secondData[0] = 0x11121314; + secondData[1] = 0x15161718; + secondDataBytes = Bytes.from(secondData); + } + + bytes memory thirdDataBytes; + { + uint32[] memory thirdData = new uint32[](3); + thirdData[0] = 0x191a1b1c; + thirdData[1] = 0x1d1e1f20; + thirdData[2] = 0x21222324; + thirdDataBytes = Bytes.from(thirdData); + } + + bytes32 encodedDynamicLength; + { + uint16[] memory dynamicLengths = new uint16[](2); + dynamicLengths[0] = uint16(secondDataBytes.length); + dynamicLengths[1] = uint16(thirdDataBytes.length); + encodedDynamicLength = StoreCore.encodeDynamicDataLength(dynamicLengths); + } + + // Concat data + bytes memory data = bytes.concat(firstDataBytes, secondDataBytes, thirdDataBytes); + + // Create key + bytes32[] memory key = new bytes32[](1); + key[0] = bytes32("some.key"); + + // Set data + uint256 gas = gasleft(); + StoreCore.set(table, key, encodedDynamicLength, data); + gas = gas - gasleft(); + console.log("gas used (set, %s bytes, 1 static field, 2 dynamic fields): %s", data.length, gas); + + // Get data + gas = gasleft(); + bytes memory loadedData = StoreCore.get(table, key); + gas = gas - gasleft(); + console.log("gas used (get, warm, %s bytes): %s", loadedData.length, gas); + + assertEq(loadedData.length, data.length); + assertEq(keccak256(loadedData), keccak256(data)); + + // Compare gas - setting the data as raw struct + TestStruct memory _testStruct = TestStruct(0, new uint32[](2), new uint32[](3)); + _testStruct.firstData = 0x0102030405060708090a0b0c0d0e0f10; + _testStruct.secondData[0] = 0x11121314; + _testStruct.secondData[1] = 0x15161718; + _testStruct.thirdData[0] = 0x191a1b1c; + _testStruct.thirdData[1] = 0x1d1e1f20; + _testStruct.thirdData[2] = 0x21222324; + + gas = gasleft(); + testStruct = _testStruct; + gas = gas - gasleft(); + console.log("gas used (store native struct): %s", gas); + } +} + +struct TestStruct { + uint128 firstData; + uint32[] secondData; + uint32[] thirdData; } From 2cf2a82029e3906ccbf8b24913aeae3f8e9e89f1 Mon Sep 17 00:00:00 2001 From: alvrs Date: Fri, 27 Jan 2023 15:38:21 +0100 Subject: [PATCH 19/82] feat(solecs): wip data model - add logic and test to set and get individual fields --- packages/solecs/v2/Memory.sol | 8 +- packages/solecs/v2/Storage.sol | 35 ++--- packages/solecs/v2/StoreCore.sol | 76 +++++++++-- packages/solecs/v2/StoreView.sol | 6 +- packages/solecs/v2/test/StoreCore.t.sol | 164 ++++++++++++++++++++++-- 5 files changed, 251 insertions(+), 38 deletions(-) diff --git a/packages/solecs/v2/Memory.sol b/packages/solecs/v2/Memory.sol index 7b81477458..1ea2d4f8fb 100644 --- a/packages/solecs/v2/Memory.sol +++ b/packages/solecs/v2/Memory.sol @@ -25,9 +25,15 @@ library Memory { } } - function ptr(bytes memory data) internal pure returns (uint256 memoryPointer) { + function dataPointer(bytes memory data) internal pure returns (uint256 memoryPointer) { assembly { memoryPointer := add(data, 0x20) } } + + function lengthPointer(bytes memory data) internal pure returns (uint256 memoryPointer) { + assembly { + memoryPointer := data + } + } } diff --git a/packages/solecs/v2/Storage.sol b/packages/solecs/v2/Storage.sol index e65c90b663..7ec86473c3 100644 --- a/packages/solecs/v2/Storage.sol +++ b/packages/solecs/v2/Storage.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; +import { console } from "forge-std/console.sol"; import { Utils } from "./Utils.sol"; import { Bytes } from "./Bytes.sol"; import { Memory } from "./Memory.sol"; @@ -68,14 +69,14 @@ library Storage { for (uint256 i; i < numWords; i++) { // If this is the first word, and there is an offset, apply a mask to beginning if ((i == 0 && offset > 0)) { - uint256 _length = length > 32 ? 32 - offset : length; // // the number of bytes to write + uint256 _lengthToWrite = length + offset > 32 ? 32 - offset : length; // // the number of bytes to write _writePartialWord( storagePointer, // the word to update offset, // the offset in bytes to start writing - _length, + _lengthToWrite, Memory.read(memoryPointer) // Pass the first 32 bytes of the data ); - bytesWritten += _length; + bytesWritten += _lengthToWrite; // If this is the last word, and there is a partial word, apply a mask to the end } else if (i == numWords - 1 && (length + offset) % 32 > 0) { _writePartialWord( @@ -125,9 +126,9 @@ library Storage { uint256 offset, uint256 length ) internal view returns (bytes memory) { - Buffer buf = Buffer_.allocate(uint128(length)); - read(storagePointer, offset, length, buf); - return buf.toBytes(); + Buffer buffer = Buffer_.allocate(uint128(length)); + read(storagePointer, offset, length, buffer); + return buffer.toBytes(); } /** @@ -137,39 +138,39 @@ library Storage { uint256 storagePointer, uint256 offset, uint256 length, - Buffer buf + Buffer buffer ) internal view { uint256 numWords = Utils.divCeil(length + offset, 32); - uint256 _length; + uint256 _lengthToRead; for (uint256 i; i < numWords; i++) { // If this is the first word, and there is an offset, apply a mask to beginning (and possibly the end if length + offset is less than 32) if ((i == 0 && offset > 0)) { - _length = length > 32 ? 32 - offset : length; // the number of bytes to read - buf.appendUnchecked( + _lengthToRead = length + offset > 32 ? 32 - offset : length; // the number of bytes to read + buffer.appendUnchecked( _loadPartialWord( storagePointer, // the slot to start loading from offset, // the offset in bytes to start reading from - _length + _lengthToRead ), - uint128(_length) + uint128(_lengthToRead) ); // If this is the last word, and there is a partial word, apply a mask to the end } else if (i == numWords - 1 && (length + offset) % 32 > 0) { - _length = (length + offset) % 32; // the relevant length of the trailing word - buf.appendUnchecked( + _lengthToRead = (length + offset) % 32; // the relevant length of the trailing word + buffer.appendUnchecked( _loadPartialWord( storagePointer + i, // the word to read from 0, // the offset in bytes to start reading from - _length + _lengthToRead ), - uint128(_length) + uint128(_lengthToRead) ); // Else, just read the word } else { - buf.appendUnchecked(_loadWord(storagePointer + i), 32); + buffer.appendUnchecked(_loadWord(storagePointer + i), 32); } } } diff --git a/packages/solecs/v2/StoreCore.sol b/packages/solecs/v2/StoreCore.sol index 7154ddbc42..ba7a767a82 100644 --- a/packages/solecs/v2/StoreCore.sol +++ b/packages/solecs/v2/StoreCore.sol @@ -8,6 +8,12 @@ import { Memory } from "./Memory.sol"; import { console } from "forge-std/console.sol"; import "./Buffer.sol"; +// TODO +// - Make schema a custom data type we can execute methods on, move schema methods to schema library +// - Turn all storage pointer to uint256 for consistency (uint256 is better than bytes32 because it's easier to do arithmetic on) +// - Change Storage library functions to make it clearer which argument is offset and which is length +// - Streamline naming in Storage and Memory libraries (probably just use load and store instead of read and write?) + 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 StoreUpdate(bytes32 table, bytes32[] key, uint8 schemaIndex, bytes data); @@ -82,7 +88,7 @@ library StoreCore { // Store the static data at the static data location bytes32 staticDataLocation = _getStaticDataLocation(table, key); - uint256 memoryPointer = Memory.ptr(data); + uint256 memoryPointer = Memory.dataPointer(data); Storage.write(staticDataLocation, 0, memoryPointer, staticLength); memoryPointer += staticLength; // move the memory pointer to the start of the dynamic data @@ -244,9 +250,7 @@ library StoreCore { * Get full static record for the given table and key tuple (loading schema from storage) */ function getStaticData(bytes32 table, bytes32[] memory key) internal view returns (bytes memory) { - // Get schema for this table bytes32 schema = getSchema(table); - return getStaticData(table, key, schema); } @@ -263,13 +267,67 @@ library StoreCore { return Storage.read(location, _getStaticDataLength(schema)); } - // TODO + /** + * Get a single field from the given table and key tuple (loading schema from storage) + */ function getField( - bytes32, - bytes32[] memory, - uint8 - ) internal pure returns (bytes memory) { - revert StoreCore_NotImplemented(); + bytes32 table, + bytes32[] memory key, + uint8 schemaIndex + ) internal view returns (bytes memory) { + bytes32 schema = getSchema(table); + return getField(table, key, schemaIndex, schema); + } + + /** + * Get a single field from the given table and key tuple, with the given schema + */ + function getField( + bytes32 table, + bytes32[] memory key, + uint8 schemaIndex, + bytes32 schema + ) internal view returns (bytes memory) { + if (schemaIndex < _getNumStaticFields(schema)) { + return _getStaticField(table, key, schemaIndex, schema); + } else { + return _getDynamicField(table, key, schemaIndex, schema); + } + } + + /** + * Get a single static field from the given table and key tuple, with the given schema + */ + function _getStaticField( + bytes32 table, + bytes32[] memory key, + uint8 schemaIndex, + bytes32 schema + ) internal view returns (bytes memory) { + // Get the length, storage location and offset of the static field + SchemaType schemaType = _getSchemaTypeAtIndex(schema, schemaIndex); + uint256 dataLength = getStaticByteLength(schemaType); + uint256 location = uint256(_getStaticDataLocation(table, key)); + uint256 offset = _getStaticDataOffset(schema, schemaIndex); + + // Load the data from storage + return Storage.read(location, offset, dataLength); + } + + /** + * Get a single dynamic field from the given table and key tuple, with the given schema + */ + function _getDynamicField( + bytes32 table, + bytes32[] memory key, + uint8 schemaIndex, + bytes32 schema + ) internal view returns (bytes memory) { + // Get the length and storage location of the dynamic field + uint8 dynamicSchemaIndex = schemaIndex - _getNumStaticFields(schema); + uint256 dataLength = _getDynamicDataLengthAtIndex(table, key, dynamicSchemaIndex); + bytes32 location = _getDynamicDataLocation(table, key, dynamicSchemaIndex); + return Storage.read(location, dataLength); } /************************************************************************ diff --git a/packages/solecs/v2/StoreView.sol b/packages/solecs/v2/StoreView.sol index 607ced1098..5ecdc0f21e 100644 --- a/packages/solecs/v2/StoreView.sol +++ b/packages/solecs/v2/StoreView.sol @@ -5,7 +5,7 @@ import { SchemaType } from "./Types.sol"; import { IStore } from "./IStore.sol"; import { StoreCore } from "./StoreCore.sol"; -// Not abstract, so that it can be used as a base contract for testing and if write access is not needed +// Not abstract, so that it can be used as a base contract for testing and wherever write access is not needed contract StoreView is IStore { error Store_BaseContractNotImplemented(); @@ -66,7 +66,7 @@ contract StoreView is IStore { } // Get full record (including full array) - function get(bytes32 table, bytes32[] memory key) public view returns (bytes memory data) { + function get(bytes32 table, bytes32[] memory key) public view virtual returns (bytes memory data) { data = StoreCore.get(table, key); } @@ -75,7 +75,7 @@ contract StoreView is IStore { bytes32 table, bytes32[] memory key, uint8 schemaIndex - ) public pure returns (bytes memory data) { + ) public view virtual returns (bytes memory data) { data = StoreCore.getField(table, key, schemaIndex); } diff --git a/packages/solecs/v2/test/StoreCore.t.sol b/packages/solecs/v2/test/StoreCore.t.sol index ca15b947dd..77f5f18b55 100644 --- a/packages/solecs/v2/test/StoreCore.t.sol +++ b/packages/solecs/v2/test/StoreCore.t.sol @@ -7,9 +7,20 @@ import { StoreCore } from "../StoreCore.sol"; import { Utils } from "../Utils.sol"; import { Bytes } from "../Bytes.sol"; import { SchemaType } from "../Types.sol"; +import { Storage } from "../Storage.sol"; +import { Memory } from "../Memory.sol"; +import { Cast } from "../Cast.sol"; +import "../Buffer.sol"; + +struct TestStruct { + uint128 firstData; + uint32[] secondData; + uint32[] thirdData; +} contract StoreCoreTest is DSTestPlus { TestStruct testStruct; + mapping(uint256 => bytes) testMapping; function testGetStaticDataLength() public { bytes32 schema = bytes32( @@ -319,13 +330,13 @@ contract StoreCoreTest is DSTestPlus { uint256 gas = gasleft(); StoreCore.set(table, key, encodedDynamicLength, data); gas = gas - gasleft(); - console.log("gas used (set, %s bytes, 1 static field, 2 dynamic fields): %s", data.length, gas); + console.log("gas used (store complex struct / StoreCore): %s", gas); // Get data gas = gasleft(); bytes memory loadedData = StoreCore.get(table, key); gas = gas - gasleft(); - console.log("gas used (get, warm, %s bytes): %s", loadedData.length, gas); + // console.log("gas used (read using StoreCore): %s", gas); assertEq(loadedData.length, data.length); assertEq(keccak256(loadedData), keccak256(data)); @@ -342,12 +353,149 @@ contract StoreCoreTest is DSTestPlus { gas = gasleft(); testStruct = _testStruct; gas = gas - gasleft(); - console.log("gas used (store native struct): %s", gas); + console.log("gas used (store complex struct / Native): %s", gas); + + gas = gasleft(); + testMapping[1234] = abi.encode(_testStruct); + gas = gas - gasleft(); + console.log("gas used (store complex struct / abi.encode): %s", gas); } -} -struct TestStruct { - uint128 firstData; - uint32[] secondData; - uint32[] thirdData; + function testSetAndGetField() public { + bytes32 table = keccak256("some.table"); + + { + // Register table's schema + SchemaType[] memory schemaTypes = new SchemaType[](4); + schemaTypes[0] = SchemaType.Uint128; + schemaTypes[1] = SchemaType.Uint256; + schemaTypes[2] = SchemaType.Uint32Array; + schemaTypes[3] = SchemaType.Uint32Array; + bytes32 schema = StoreCore.encodeSchema(schemaTypes); + StoreCore.registerSchema(table, schema); + } + + bytes16 firstDataBytes = bytes16(0x0102030405060708090a0b0c0d0e0f10); + + // Create key + bytes32[] memory key = new bytes32[](1); + key[0] = bytes32("some.key"); + + // Set first field + uint256 gas = gasleft(); + StoreCore.setField(table, key, 0, bytes.concat(firstDataBytes)); + gas = gas - gasleft(); + console.log("gas used (set uint128, no offset): %s", gas); + + //////////////// + // Static data + //////////////// + + // Get first field + gas = gasleft(); + bytes memory loadedData = StoreCore.getField(table, key, 0); + gas = gas - gasleft(); + console.log("gas used (get uint128, no offset): %s", gas); + + // Verify loaded data is correct + assertEq(loadedData.length, 16); + assertEq(bytes16(loadedData), bytes16(firstDataBytes)); + + // Verify the second index is not set yet + assertEq(uint256(bytes32(StoreCore.getField(table, key, 1))), 0); + + // Set second field + bytes32 secondDataBytes = keccak256("some data"); + + gas = gasleft(); + StoreCore.setField(table, key, 1, bytes.concat(secondDataBytes)); + gas = gas - gasleft(); + console.log("gas used (set uint256, 128 offset): %s", gas); + + // Get second field + gas = gasleft(); + loadedData = StoreCore.getField(table, key, 1); + gas = gas - gasleft(); + console.log("gas used (get uint256, 128 offset): %s", gas); + + // Verify loaded data is correct + assertEq(loadedData.length, 32); + assertEq(bytes32(loadedData), secondDataBytes); + + // Verify the first field didn't change + assertEq(bytes16(StoreCore.getField(table, key, 0)), bytes16(firstDataBytes)); + + // Verify the full static data is correct + assertEq(StoreCore.getStaticData(table, key).length, 48); + assertEq(Bytes.slice16(StoreCore.getStaticData(table, key), 0), firstDataBytes); + assertEq(Bytes.slice32(StoreCore.getStaticData(table, key), 16), secondDataBytes); + assertEq(keccak256(StoreCore.getStaticData(table, key)), keccak256(bytes.concat(firstDataBytes, secondDataBytes))); + + //////////////// + // Dynamic data + //////////////// + + bytes memory thirdDataBytes; + { + uint32[] memory thirdData = new uint32[](2); + thirdData[0] = 0x11121314; + thirdData[1] = 0x15161718; + thirdDataBytes = Bytes.from(thirdData); + } + + bytes memory fourthDataBytes; + { + uint32[] memory fourthData = new uint32[](3); + fourthData[0] = 0x191a1b1c; + fourthData[1] = 0x1d1e1f20; + fourthData[2] = 0x21222324; + fourthDataBytes = Bytes.from(fourthData); + } + + // Set third field + gas = gasleft(); + StoreCore.setField(table, key, 2, thirdDataBytes); + gas = gas - gasleft(); + console.log("gas used (set uint32[2]): %s", gas); + + // Get third field + gas = gasleft(); + loadedData = StoreCore.getField(table, key, 2); + gas = gas - gasleft(); + console.log("gas used (get uint32[2]): %s", gas); + + // Verify loaded data is correct + assertEq(Cast.toUint32Array(Buffer_.fromBytes(loadedData).toArray(4)).length, 2); + assertEq(loadedData.length, thirdDataBytes.length); + assertEq(keccak256(loadedData), keccak256(thirdDataBytes)); + + // Verify the fourth field is not set yet + assertEq(StoreCore.getField(table, key, 3).length, 0); + + // Verify none of the previous fields were impacted + assertEq(bytes16(StoreCore.getField(table, key, 0)), bytes16(firstDataBytes)); + assertEq(bytes32(StoreCore.getField(table, key, 1)), bytes32(secondDataBytes)); + + // Set fourth field + gas = gasleft(); + StoreCore.setField(table, key, 3, fourthDataBytes); + gas = gas - gasleft(); + console.log("gas used (set uint32[3]): %s", gas); + + // Get fourth field + gas = gasleft(); + loadedData = StoreCore.getField(table, key, 3); + gas = gas - gasleft(); + console.log("gas used (get uint32[3]): %s", gas); + + // Verify loaded data is correct + assertEq(loadedData.length, fourthDataBytes.length); + assertEq(keccak256(loadedData), keccak256(fourthDataBytes)); + + // Verify all fields are correct + assertEq( + keccak256(StoreCore.get(table, key)), + keccak256(bytes.concat(firstDataBytes, secondDataBytes, thirdDataBytes, fourthDataBytes)) + ); + } } From 7d8b557c6544750fa427247ffe52a58cdf583d93 Mon Sep 17 00:00:00 2001 From: alvrs Date: Fri, 27 Jan 2023 15:45:44 +0100 Subject: [PATCH 20/82] test(solecs): wip data model - add fuzzy test for Storage lib --- packages/solecs/v2/Storage.sol | 8 ++++++++ packages/solecs/v2/test/Storage.t.sol | 9 +++++++++ 2 files changed, 17 insertions(+) diff --git a/packages/solecs/v2/Storage.sol b/packages/solecs/v2/Storage.sol index 7ec86473c3..33c25c71cf 100644 --- a/packages/solecs/v2/Storage.sol +++ b/packages/solecs/v2/Storage.sol @@ -63,6 +63,10 @@ library Storage { uint256 memoryPointer, uint256 length ) internal { + // Support offsets that are greater than 32 bytes by incrementing the storagePointer and decrementing the offset + storagePointer += offset / 32; + offset %= 32; + uint256 numWords = Utils.divCeil(length + offset, 32); uint256 bytesWritten; @@ -140,6 +144,10 @@ library Storage { uint256 length, Buffer buffer ) internal view { + // Support offsets that are greater than 32 bytes by incrementing the storagePointer and decrementing the offset + storagePointer += offset / 32; + offset %= 32; + uint256 numWords = Utils.divCeil(length + offset, 32); uint256 _lengthToRead; diff --git a/packages/solecs/v2/test/Storage.t.sol b/packages/solecs/v2/test/Storage.t.sol index 316eb254d6..b4290af7e6 100644 --- a/packages/solecs/v2/test/Storage.t.sol +++ b/packages/solecs/v2/test/Storage.t.sol @@ -68,4 +68,13 @@ contract StorageTest is DSTestPlus { assertEq(Bytes.slice1(data, 33), bytes1(0x04)); assertEq(keccak256(data), keccak256(data1)); } + + function testWriteReadFuzzy( + bytes memory data, + bytes32 storagePointer, + uint8 offset + ) public { + Storage.write(storagePointer, offset, data); + assertEq(keccak256(Storage.read(storagePointer, offset, data.length)), keccak256(data)); + } } From 018014b321277a1a304e331b413c7bc33c0696e3 Mon Sep 17 00:00:00 2001 From: alvrs Date: Fri, 27 Jan 2023 16:06:37 +0100 Subject: [PATCH 21/82] test(solecs): wip data model - add fuzzy tests for Buffer and Bytes --- packages/solecs/v2/test/Buffer.t.sol | 56 ++++++++++++++++++++++++++++ packages/solecs/v2/test/Bytes.t.sol | 51 +++++++++---------------- 2 files changed, 74 insertions(+), 33 deletions(-) diff --git a/packages/solecs/v2/test/Buffer.t.sol b/packages/solecs/v2/test/Buffer.t.sol index 0da64587e4..3bf504ed0a 100644 --- a/packages/solecs/v2/test/Buffer.t.sol +++ b/packages/solecs/v2/test/Buffer.t.sol @@ -44,6 +44,13 @@ contract BufferTest is DSTestPlus { assertEq(keccak256(buf.toBytes()), keccak256(data)); } + function testFromBytesFuzzy(bytes memory data) public { + Buffer buf = Buffer_.fromBytes(data); + assertEq(uint256(buf.length()), data.length); + assertEq(uint256(buf.capacity()), data.length); + assertEq(keccak256(buf.toBytes()), keccak256(data)); + } + function testSetLength() public { Buffer buf = Buffer_.allocate(32); @@ -61,6 +68,13 @@ contract BufferTest is DSTestPlus { assertEq(uint256(buf.capacity()), 32); } + function testSetLengthFuzzy(bytes memory data, uint16 length) public { + Buffer buf = Buffer_.fromBytes(data); + assertEq(uint256(buf.capacity()), data.length); + buf._setLengthUnchecked(length); + assertEq(uint256(buf.length()), length); + } + function testFailSetLength() public pure { Buffer buf = Buffer_.allocate(32); buf._setLength(33); @@ -86,6 +100,48 @@ contract BufferTest is DSTestPlus { assertEq(buf.read8(8), bytes8(0x090a0b0c0d0e0f10)); } + function testAppendFuzzy( + bytes memory input, + bytes memory append1, + bytes memory append2 + ) public { + Buffer buffer = Buffer_.allocate(uint128(input.length + append1.length + append2.length)); + buffer.append(input); + buffer.append(append1); + buffer.append(append2); + assertEq(keccak256(buffer.toBytes()), keccak256(bytes.concat(input, append1, append2))); + } + + function testCompareGasToConcat() public { + bytes memory data1 = bytes.concat(keccak256("data1")); + bytes memory data2 = bytes.concat(keccak256("data2")); + bytes memory data3 = bytes.concat(keccak256("data3")); + + uint256 gas = gasleft(); + Buffer buf = Buffer_.allocate(uint128(data1.length + data2.length + data3.length)); + buf.append(data1); + buf.append(data2); + buf.append(data3); + gas = gas - gasleft(); + console.log("gas used (buffer concat): %s", gas); + + gas = gasleft(); + bytes memory dataAllAtOnce = bytes.concat(data1, data2, data3); + gas = gas - gasleft(); + console.log("gas used (concat all at once): %s", gas); + + gas = gasleft(); + bytes memory dataSeparately = new bytes(0); + dataSeparately = bytes.concat(dataSeparately, data1); + dataSeparately = bytes.concat(dataSeparately, data2); + dataSeparately = bytes.concat(dataSeparately, data3); + gas = gas - gasleft(); + console.log("gas used (concat separately): %s", gas); + + assertEq(keccak256(buf.toBytes()), keccak256(dataAllAtOnce)); + assertEq(keccak256(buf.toBytes()), keccak256(dataSeparately)); + } + function testAppendFixed() public { Buffer buf = Buffer_.allocate(32); bytes32 data1 = bytes32(bytes8(0x0102030405060708)); diff --git a/packages/solecs/v2/test/Bytes.t.sol b/packages/solecs/v2/test/Bytes.t.sol index 547c683bbc..482fd435c3 100644 --- a/packages/solecs/v2/test/Bytes.t.sol +++ b/packages/solecs/v2/test/Bytes.t.sol @@ -95,7 +95,7 @@ contract BytesTest is DSTestPlus { assertEq(uint256(output), 0x0100000000000000000000000000000000000000000000000000000000000002); } - function testToBytesArray() public { + function testToBytes32Array() public { bytes memory input = new bytes(64); input[0] = 0x01; input[31] = 0x02; @@ -110,7 +110,7 @@ contract BytesTest is DSTestPlus { assertEq(uint256(output[1]), 0x0300000000000000000000000000000000000000000000000000000000000004); } - function testToBytesArrayUneven() public { + function testToBytes32ArrayUneven() public { bytes memory input = new bytes(65); input[0] = 0x01; input[31] = 0x02; @@ -145,6 +145,10 @@ contract BytesTest is DSTestPlus { assertEq(output2, input); } + function testFromAndToUint32Fuzzy(uint32 input) public { + assertEq(Bytes.toUint32(Bytes.from(input)), input); + } + function testFromAndToAddress() public { address input = address(0x0100000000000000000000000000000000000002); @@ -163,6 +167,10 @@ contract BytesTest is DSTestPlus { assertEq(output2, input); } + function testFromAndToAddressFuzzy(address input) public { + assertEq(Bytes.toAddress(Bytes.from(input)), input); + } + function testFromAndToUint8() public { uint8 input = 0x02; @@ -181,6 +189,10 @@ contract BytesTest is DSTestPlus { assertEq(output2, input); } + function testFromAndToUint8Fuzzy(uint8 input) public { + assertEq(Bytes.toUint8(Bytes.fromUint8(input)), input); + } + function testFromAndToBytes4() public { bytes4 input = bytes4(0x01000002); @@ -199,6 +211,10 @@ contract BytesTest is DSTestPlus { assertEq(output2, input); } + function testFromAndToBytes4Fuzzy(bytes4 input) public { + assertEq(Bytes.toBytes4(Bytes.from(input)), input); + } + function testEquals() public { bytes memory a = bytes("a"); bytes memory b = bytes("a"); @@ -289,37 +305,6 @@ contract BytesTest is DSTestPlus { assertEq(output, original); } - function testAbiEncoding() public view { - bytes memory test = bytes.concat(bytes2(0x0102)); - console.log("bytes2 raw length: %s", test.length); - console.log("bytes2 abi encoded length: %s", abi.encode(test).length); - - string memory test2 = "max length of a string in a word"; - console.log("string raw length: %s", bytes(test2).length); - console.log("string abi encoded length: %s", abi.encode(test2).length); - - bytes[] memory test3 = new bytes[](2); - test3[0] = bytes.concat(bytes1(0x01)); - test3[1] = bytes.concat(bytes2(0x0203)); - - console.log("bytes[] raw length: %s", Bytes.from(test3).length); - console.log("bytes[] abi encoded length: %s", abi.encode(test3).length); - } - - function testEncodeDecode() public { - uint16[] memory input1 = new uint16[](2); - input1[0] = 0x0102; - input1[1] = 0x0304; - uint8[] memory input2 = new uint8[](2); - input2[0] = 0x05; - input2[1] = 0x06; - - bytes32 lengths = bytes32(bytes.concat(bytes2(uint16(input1.length)), bytes2(uint16(input2.length)))); - assertEq(lengths, bytes32(bytes4(0x00020002))); - - bytes memory input = bytes.concat(lengths, Bytes.from(input1), Bytes.from(input2)); - } - function testSetBytes1() public { bytes32 input = bytes32(0); From a3b650132e0ba5db6381ec81214c6bc237ced8b1 Mon Sep 17 00:00:00 2001 From: alvrs Date: Fri, 27 Jan 2023 16:42:20 +0100 Subject: [PATCH 22/82] feat(solecs): wip data model - add validity checks for registering schemas --- packages/solecs/v2/StoreCore.sol | 80 ++++++++++++++++++++++--- packages/solecs/v2/test/StoreCore.t.sol | 79 ++++++++++++++++++++++++ 2 files changed, 150 insertions(+), 9 deletions(-) diff --git a/packages/solecs/v2/StoreCore.sol b/packages/solecs/v2/StoreCore.sol index ba7a767a82..055178ece7 100644 --- a/packages/solecs/v2/StoreCore.sol +++ b/packages/solecs/v2/StoreCore.sol @@ -22,6 +22,8 @@ library StoreCore { bytes32 internal constant SCHEMA_TABLE = keccak256("mud.store.table.schema"); error StoreCore_SchemaTooLong(); + error StoreCore_StaticTypeAfterDynamicType(); + error StoreCore_TableAlreadyExists(bytes32 table); error StoreCore_NotImplemented(); error StoreCore_InvalidDataLength(uint256 expected, uint256 received); @@ -35,15 +37,57 @@ library StoreCore { * Check if the given table exists */ function hasTable(bytes32 table) internal view returns (bool) { - // TODO + return getSchema(table) != bytes32(0); + } + + /** + * Register a new table schema + */ + function registerSchema(bytes32 table, SchemaType[] memory schema) internal { + // Verify the schema doesn't exist yet + if (hasTable(table)) { + revert StoreCore_TableAlreadyExists(table); + } + + // Register the schema (validity checks are done in encodeSchema) + _registerSchemaUnchecked(table, encodeSchema(schema)); } /** * Register a new table schema */ function registerSchema(bytes32 table, bytes32 schema) internal { - // TODO: verify the table doesn't already exist - // TODO: verify the schema is valid (no dynamic elements in static section, etc) + // Verify the schema doesn't exist yet + if (hasTable(table)) { + revert StoreCore_TableAlreadyExists(table); + } + + // Verify the schema is not too long + uint256 numFields = _getNumStaticFields(schema) + _getNumDynamicFields(schema); + if (numFields > 28) { + revert StoreCore_SchemaTooLong(); + } + + // Verify all static fields come before the dynamic fields + bool hasDynamicField; + for (uint256 i = 0; i < numFields; i++) { + if (getStaticByteLength(_getSchemaTypeAtIndex(schema, i)) == 0) { + // Flag that we've seen the first dynamic field + hasDynamicField = true; + } else if (hasDynamicField) { + // We've seen a dynamic field, but this field is static + revert StoreCore_StaticTypeAfterDynamicType(); + } + } + + // Verify all dynamic fields come after the static fields + _registerSchemaUnchecked(table, schema); + } + + /** + * Register a new table schema without validity checks + */ + function _registerSchemaUnchecked(bytes32 table, bytes32 schema) internal { bytes32[] memory key = new bytes32[](1); key[0] = table; bytes32 location = _getStaticDataLocation(SCHEMA_TABLE, key); @@ -341,7 +385,7 @@ library StoreCore { * - 2 bytes static length of the schema * - 1 byte for number of static size fields * - 1 byte for number of dynamic size fields - * - 28 bytes for 28 schema types + * - 28 bytes for 28 schema types (max 14 dynamic fields to we can pack their lengths into 1 word) */ function encodeSchema(SchemaType[] memory _schema) internal pure returns (bytes32 schema) { if (_schema.length > 28) revert StoreCore_SchemaTooLong(); @@ -350,9 +394,20 @@ library StoreCore { // Compute the length of the schema and the number of static fields // and store the schema types in the encoded schema + bool hasDynamicFields; for (uint256 i = 0; i < _schema.length; ) { uint16 staticByteLength = uint16(getStaticByteLength(_schema[i])); - if (staticByteLength > 0) staticFields++; + + // Increase the static field count if the field is static + if (staticByteLength > 0) { + // Revert if we have seen a dynamic field before, but now we see a static field + if (hasDynamicFields) revert StoreCore_StaticTypeAfterDynamicType(); + staticFields++; + } else { + // Flag that we have seen a dynamic field + hasDynamicFields = true; + } + length += staticByteLength; schema = Bytes.setBytes1(schema, i + 4, bytes1(uint8(_schema[i]))); unchecked { @@ -360,14 +415,24 @@ library StoreCore { } } + // Require max 14 dynamic fields + uint8 dynamicFields = uint8(_schema.length) - staticFields; + if (dynamicFields > 14) revert StoreCore_SchemaTooLong(); + // Store total static length, and number of static and dynamic fields schema = Bytes.setBytes1(schema, 0, bytes1(bytes2(length))); // upper length byte schema = Bytes.setBytes1(schema, 1, bytes1(uint8(length))); // lower length byte schema = Bytes.setBytes1(schema, 2, bytes1(staticFields)); // number of static fields - schema = Bytes.setBytes1(schema, 3, bytes1(uint8(_schema.length) - staticFields)); // number of dynamic fields + schema = Bytes.setBytes1(schema, 3, bytes1(dynamicFields)); // number of dynamic fields } + /** + * Encode the given dynamic data lengths into a single bytes32 + * - 4 bytes for total length of the dynamic data + * - 2 bytes for each dynamic data length -> max 14 dynamic fields + */ function encodeDynamicDataLength(uint16[] memory lengths) internal pure returns (bytes32 encodedLength) { + if (lengths.length > 14) revert StoreCore_SchemaTooLong(); uint32 totalLength; // Compute the total length of the dynamic data @@ -396,7 +461,6 @@ library StoreCore { /** * Compute the storage location based on table id and index tuple - * TODO: provide different overloads for single key and some fixed length array keys for better devex */ function _getStaticDataLocation(bytes32 table, bytes32[] memory key) internal pure returns (bytes32) { return keccak256(abi.encode(SLOT, table, key)); @@ -404,7 +468,6 @@ library StoreCore { /** * Get storage offset for the given schema and (static length) index - * TODO: gas optimize */ function _getStaticDataOffset(bytes32 schema, uint8 schemaIndex) internal pure returns (uint256) { uint256 offset = 0; // skip length @@ -466,7 +529,6 @@ library StoreCore { /** * Get the length of the dynamic data for the given schema and index - * TODO: add tests */ function _loadEncodedDynamicDataLength(bytes32 table, bytes32[] memory key) internal view returns (bytes32) { // Load dynamic data length from storage diff --git a/packages/solecs/v2/test/StoreCore.t.sol b/packages/solecs/v2/test/StoreCore.t.sol index 77f5f18b55..0a026009c9 100644 --- a/packages/solecs/v2/test/StoreCore.t.sol +++ b/packages/solecs/v2/test/StoreCore.t.sol @@ -98,6 +98,85 @@ contract StoreCoreTest is DSTestPlus { assertEq(uint8(Bytes.slice1(loadedSchema, 5)), uint8(SchemaType.Uint16)); } + function testFailInvalidSchemaStaticAfterDynamic() public { + bytes32 table = keccak256("table"); + SchemaType[] memory schema = new SchemaType[](3); + schema[0] = SchemaType.Uint8; + schema[1] = SchemaType.Uint32Array; + schema[2] = SchemaType.Uint16; + StoreCore.registerSchema(table, schema); + } + + function testRegisterSchemaMaxDynamic() public { + bytes32 table = keccak256("table"); + SchemaType[] memory schema = new SchemaType[](14); + schema[0] = SchemaType.Uint32Array; + schema[1] = SchemaType.Uint32Array; + schema[2] = SchemaType.Uint32Array; + schema[3] = SchemaType.Uint32Array; + schema[4] = SchemaType.Uint32Array; + schema[5] = SchemaType.Uint32Array; + schema[6] = SchemaType.Uint32Array; + schema[7] = SchemaType.Uint32Array; + schema[8] = SchemaType.Uint32Array; + schema[9] = SchemaType.Uint32Array; + schema[10] = SchemaType.Uint32Array; + schema[11] = SchemaType.Uint32Array; + schema[12] = SchemaType.Uint32Array; + schema[13] = SchemaType.Uint32Array; + StoreCore.registerSchema(table, schema); + } + + function testFailInvalidSchemaTooManyDynamic() public { + bytes32 table = keccak256("table"); + SchemaType[] memory schema = new SchemaType[](15); + schema[0] = SchemaType.Uint32Array; + schema[1] = SchemaType.Uint32Array; + schema[2] = SchemaType.Uint32Array; + schema[3] = SchemaType.Uint32Array; + schema[4] = SchemaType.Uint32Array; + schema[5] = SchemaType.Uint32Array; + schema[6] = SchemaType.Uint32Array; + schema[7] = SchemaType.Uint32Array; + schema[8] = SchemaType.Uint32Array; + schema[9] = SchemaType.Uint32Array; + schema[10] = SchemaType.Uint32Array; + schema[11] = SchemaType.Uint32Array; + schema[12] = SchemaType.Uint32Array; + schema[13] = SchemaType.Uint32Array; + schema[14] = SchemaType.Uint32Array; + StoreCore.registerSchema(table, schema); + } + + function testHasSchema() public { + bytes32 schema = bytes32( + bytes.concat( + bytes2(uint16(6)), + bytes1(uint8(SchemaType.Uint8)), + bytes1(uint8(SchemaType.Uint16)), + bytes1(uint8(SchemaType.Uint8)), + bytes1(uint8(SchemaType.Uint16)) + ) + ); + + bytes32 table = keccak256("some.table"); + bytes32 table2 = keccak256("other.table"); + StoreCore.registerSchema(table, schema); + + uint256 gas = gasleft(); + StoreCore.hasTable(table); + gas = gas - gasleft(); + console.log("gas used (has): %s", gas); + + gas = gasleft(); + StoreCore.hasTable(table2); + gas = gas - gasleft(); + console.log("gas used (has not): %s", gas); + + assertTrue(StoreCore.hasTable(table)); + assertFalse(StoreCore.hasTable(table2)); + } + function testEncodeAndDecodeDynamicLength() public { uint16[] memory lengths = new uint16[](4); lengths[0] = 1; From 4e6f45293469c0d4f3691b7aa34c40ac58be860c Mon Sep 17 00:00:00 2001 From: alvrs Date: Sun, 29 Jan 2023 15:59:45 +0100 Subject: [PATCH 23/82] feat(solecs): wip data model - move schema and packed counter to own library/types --- packages/solecs/v2/Buffer.sol | 41 ++- packages/solecs/v2/IStore.sol | 8 +- packages/solecs/v2/PackedCounter.sol | 100 +++++++ packages/solecs/v2/Schema.sol | 228 +++++++++++++++ packages/solecs/v2/StoreCore.sol | 308 ++++---------------- packages/solecs/v2/StoreSwitch.sol | 8 +- packages/solecs/v2/StoreView.sol | 8 +- packages/solecs/v2/World.sol | 6 +- packages/solecs/v2/tables/RouteTable.sol | 13 +- packages/solecs/v2/tables/Vector2Table.sol | 8 +- packages/solecs/v2/test/PackedCounter.t.sol | 74 +++++ packages/solecs/v2/test/RouteTable.t.sol | 5 +- packages/solecs/v2/test/Schema.t.sol | 241 +++++++++++++++ packages/solecs/v2/test/StoreCore.t.sol | 254 +++------------- packages/solecs/v2/test/Vector2Table.t.sol | 5 +- 15 files changed, 796 insertions(+), 511 deletions(-) create mode 100644 packages/solecs/v2/PackedCounter.sol create mode 100644 packages/solecs/v2/Schema.sol create mode 100644 packages/solecs/v2/test/PackedCounter.t.sol create mode 100644 packages/solecs/v2/test/Schema.t.sol diff --git a/packages/solecs/v2/Buffer.sol b/packages/solecs/v2/Buffer.sol index 077b0fd97b..fee0942103 100644 --- a/packages/solecs/v2/Buffer.sol +++ b/packages/solecs/v2/Buffer.sol @@ -10,45 +10,54 @@ pragma solidity >=0.8.0; // First 16 bytes are the pointer to the data, followed by 16 bytes of data length. type Buffer is uint256; -using BufferUtils for Buffer global; +using Buffer_ for Buffer global; -// Static functions library Buffer_ { + uint256 constant MASK_CAPACITY = uint256(type(uint128).max); + uint256 constant MASK_PTR = uint256(type(uint128).max) << 128; + + error Buffer_Overflow(uint256 capacity, uint256 requestedLength); + + /************************************************************************ + * + * STATIC FUNCTIONS + * + ************************************************************************/ + /** * @dev Allocates a new buffer with the given capacity. */ - function allocate(uint128 capacity) internal pure returns (Buffer) { - uint256 ptr; + function allocate(uint128 _capacity) internal pure returns (Buffer) { + uint256 _ptr; assembly { let buf := mload(0x40) // free memory pointer - mstore(0x40, add(buf, add(32, capacity))) // 32 bytes for the buffer header, plus the length of the buffer + mstore(0x40, add(buf, add(32, _capacity))) // 32 bytes for the buffer header, plus the length of the buffer mstore(buf, 0) // initialize length to 0 (memory is not cleared by default) - ptr := add(buf, 32) // ptr to first data byte + _ptr := add(buf, 32) // ptr to first data byte } // Pointer is stored in upper 128 bits, capacity is stored in lower 128 bits - return Buffer.wrap(((ptr << 128) | capacity)); + return Buffer.wrap(((_ptr << 128) | _capacity)); } /** * @dev Converts a bytes array to a buffer (without copying data) */ function fromBytes(bytes memory data) internal pure returns (Buffer) { - uint256 ptr; + uint256 _ptr; assembly { - ptr := add(data, 32) // ptr to first data byte + _ptr := add(data, 32) // ptr to first data byte } // Pointer is stored in upper 128 bits, length is stored in lower 128 bits - return Buffer.wrap((ptr << 128) | uint128(data.length)); + return Buffer.wrap((_ptr << 128) | uint128(data.length)); } -} -library BufferUtils { - uint256 constant MASK_CAPACITY = uint256(type(uint128).max); - uint256 constant MASK_PTR = uint256(type(uint128).max) << 128; - - error Buffer_Overflow(uint256 capacity, uint256 requestedLength); + /************************************************************************ + * + * INSTANCE FUNCTIONS + * + ************************************************************************/ /** * @dev Returns the pointer to the start of an in-memory buffer. diff --git a/packages/solecs/v2/IStore.sol b/packages/solecs/v2/IStore.sol index 16fcff3fee..de18a2e410 100644 --- a/packages/solecs/v2/IStore.sol +++ b/packages/solecs/v2/IStore.sol @@ -2,20 +2,22 @@ pragma solidity >=0.8.0; import { SchemaType } from "./Types.sol"; +import { Schema } from "./Schema.sol"; +import { PackedCounter } from "./PackedCounter.sol"; interface IStore { // note: the preimage of the tuple of keys used to index is part of the event, so it can be used by indexers event StoreUpdate(bytes32 table, bytes32[] key, uint8 schemaIndex, bytes data); - function registerSchema(bytes32 table, bytes32 schema) external; + function registerSchema(bytes32 table, Schema schema) external; - function getSchema(bytes32 table) external view returns (bytes32 schema); + function getSchema(bytes32 table) external view returns (Schema schema); // Set full record (including full dynamic data) function set( bytes32 table, bytes32[] memory key, - bytes32 encodedDynamicLength, + PackedCounter encodedDynamicLength, bytes memory data ) external; diff --git a/packages/solecs/v2/PackedCounter.sol b/packages/solecs/v2/PackedCounter.sol new file mode 100644 index 0000000000..26355f3cd7 --- /dev/null +++ b/packages/solecs/v2/PackedCounter.sol @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import { SchemaType, getStaticByteLength } from "./Types.sol"; +import { Bytes } from "./Bytes.sol"; + +// - 4 bytes accumulated counter +// - 2 bytes length per counter +type PackedCounter is bytes32; + +using PackedCounter_ for PackedCounter global; + +library PackedCounter_ { + /************************************************************************ + * + * STATIC FUNCTIONS + * + ************************************************************************/ + + /** + * Encode the given counters into a single packed counter + * - 4 bytes for the accumulated length + * - 2 bytes per counter -> max 14 counters + */ + function pack(uint16[] memory counters) internal pure returns (PackedCounter) { + bytes32 packedCounter; + uint32 accumulator; + + // Compute the sum of all counters + // and pack the counters + for (uint256 i; i < counters.length; ) { + packedCounter = Bytes.setBytes2(packedCounter, 4 + 2 * i, bytes2(counters[i])); + accumulator += counters[i]; + unchecked { + i++; + } + } + + // Store total length + packedCounter = Bytes.setBytes4(packedCounter, 0, bytes4(accumulator)); + + return PackedCounter.wrap(packedCounter); + } + + /************************************************************************ + * + * INSTANCE FUNCTIONS + * + ************************************************************************/ + + /** + * Decode the accumulated counter + * (first four bytes of packed counter) + */ + function total(PackedCounter packedCounter) internal pure returns (uint256) { + return uint256(uint32(bytes4(PackedCounter.unwrap(packedCounter)))); + } + + /** + * Decode the counter at the given index + * (two bytes per counter after the first four bytes) + */ + function atIndex(PackedCounter packedCounter, uint256 index) internal pure returns (uint256) { + uint256 offset = 4 + index * 2; + return uint256(uint16(Bytes.slice2(PackedCounter.unwrap(packedCounter), offset))); + } + + /** + * Set a counter at the given index, return the new packed counter + */ + function setAtIndex( + PackedCounter packedCounter, + uint256 index, + uint256 newValueAtIndex + ) internal pure returns (PackedCounter) { + bytes32 rawPackedCounter = PackedCounter.unwrap(packedCounter); + + // Get current lengths (total and at index) + uint256 accumulator = total(packedCounter); + uint256 currentValueAtIndex = atIndex(packedCounter, uint8(index)); + + // Compute the difference and update the total value + int256 lengthDiff = int256(newValueAtIndex) - int256(currentValueAtIndex); + accumulator = uint256(int256(accumulator) + lengthDiff); + + // Set the new accumulated value and value at index + uint256 offset = 4 + index * 2; // (4 bytes total length, 2 bytes per dynamic schema) + rawPackedCounter = Bytes.setBytes4(rawPackedCounter, 0, bytes4(uint32(accumulator))); + rawPackedCounter = Bytes.setBytes2(rawPackedCounter, offset, bytes2(uint16(newValueAtIndex))); + + return PackedCounter.wrap(rawPackedCounter); + } + + /* + * Unwrap the packed counter + */ + function unwrap(PackedCounter packedCounter) internal pure returns (bytes32) { + return PackedCounter.unwrap(packedCounter); + } +} diff --git a/packages/solecs/v2/Schema.sol b/packages/solecs/v2/Schema.sol new file mode 100644 index 0000000000..30f663cd5b --- /dev/null +++ b/packages/solecs/v2/Schema.sol @@ -0,0 +1,228 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import { SchemaType, getStaticByteLength } from "./Types.sol"; +import { Bytes } from "./Bytes.sol"; + +// - 2 bytes static length of the schema +// - 1 byte for number of static size fields +// - 1 byte for number of dynamic size fields +// - 28 bytes for 28 schema types (max 14 dynamic fields to we can pack their lengths into 1 word) +type Schema is bytes32; + +using Schema_ for Schema global; + +library Schema_ { + error Schema_InvalidLength(uint256 length); + error Schema_StaticTypeAfterDynamicType(); + + /************************************************************************ + * + * STATIC FUNCTIONS + * + ************************************************************************/ + + /** + * Encode the given schema into a single bytes32 + */ + function encode(SchemaType[] memory _schema) internal pure returns (Schema) { + if (_schema.length > 28) revert Schema_InvalidLength(_schema.length); + bytes32 schema; + uint16 length; + uint8 staticFields; + + // Compute the length of the schema and the number of static fields + // and store the schema types in the encoded schema + bool hasDynamicFields; + for (uint256 i = 0; i < _schema.length; ) { + uint16 staticByteLength = uint16(getStaticByteLength(_schema[i])); + + // Increase the static field count if the field is static + if (staticByteLength > 0) { + // Revert if we have seen a dynamic field before, but now we see a static field + if (hasDynamicFields) revert Schema_StaticTypeAfterDynamicType(); + staticFields++; + } else { + // Flag that we have seen a dynamic field + hasDynamicFields = true; + } + + length += staticByteLength; + schema = Bytes.setBytes1(schema, i + 4, bytes1(uint8(_schema[i]))); + unchecked { + i++; + } + } + + // Require max 14 dynamic fields + uint8 dynamicFields = uint8(_schema.length) - staticFields; + if (dynamicFields > 14) revert Schema_InvalidLength(dynamicFields); + + // Store total static length, and number of static and dynamic fields + schema = Bytes.setBytes1(schema, 0, bytes1(bytes2(length))); // upper length byte + schema = Bytes.setBytes1(schema, 1, bytes1(uint8(length))); // lower length byte + schema = Bytes.setBytes1(schema, 2, bytes1(staticFields)); // number of static fields + schema = Bytes.setBytes1(schema, 3, bytes1(dynamicFields)); // number of dynamic fields + + return Schema.wrap(schema); + } + + // Overrides for encode functions + function encode(SchemaType a) internal pure returns (Schema) { + SchemaType[] memory schema = new SchemaType[](1); + schema[0] = a; + return encode(schema); + } + + function encode(SchemaType a, SchemaType b) internal pure returns (Schema) { + SchemaType[] memory schema = new SchemaType[](2); + schema[0] = a; + schema[1] = b; + return encode(schema); + } + + function encode( + SchemaType a, + SchemaType b, + SchemaType c + ) internal pure returns (Schema) { + SchemaType[] memory schema = new SchemaType[](3); + schema[0] = a; + schema[1] = b; + schema[2] = c; + return encode(schema); + } + + function encode( + SchemaType a, + SchemaType b, + SchemaType c, + SchemaType d + ) internal pure returns (Schema) { + SchemaType[] memory schema = new SchemaType[](4); + schema[0] = a; + schema[1] = b; + schema[2] = c; + schema[3] = d; + return encode(schema); + } + + function encode( + SchemaType a, + SchemaType b, + SchemaType c, + SchemaType d, + SchemaType e + ) internal pure returns (Schema) { + SchemaType[] memory schema = new SchemaType[](5); + schema[0] = a; + schema[1] = b; + schema[2] = c; + schema[3] = d; + schema[4] = e; + return encode(schema); + } + + function encode( + SchemaType a, + SchemaType b, + SchemaType c, + SchemaType d, + SchemaType e, + SchemaType f + ) internal pure returns (Schema) { + SchemaType[] memory schema = new SchemaType[](6); + schema[0] = a; + schema[1] = b; + schema[2] = c; + schema[3] = d; + schema[4] = e; + schema[5] = f; + return encode(schema); + } + + /************************************************************************ + * + * INSTANCE FUNCTIONS + * + ************************************************************************/ + + /** + * Get the length of the static data for the given schema + */ + function staticDataLength(Schema schema) internal pure returns (uint256) { + return uint256(uint16(bytes2(Schema.unwrap(schema)))); + } + + /** + * Get the type of the data for the given schema at the given index + */ + function atIndex(Schema schema, uint256 index) internal pure returns (SchemaType) { + return SchemaType(uint8(Bytes.slice1(Schema.unwrap(schema), index + 4))); + } + + /** + * Get the number of dynamic length fields for the given schema + */ + function numDynamicFields(Schema schema) internal pure returns (uint8) { + return uint8(Bytes.slice1(Schema.unwrap(schema), 3)); + } + + /** + * Get the number of static fields for the given schema + */ + function numStaticFields(Schema schema) internal pure returns (uint8) { + return uint8(Bytes.slice1(Schema.unwrap(schema), 2)); + } + + /** + * Check if the given schema is empty + */ + function isEmpty(Schema schema) internal pure returns (bool) { + return Schema.unwrap(schema) == bytes32(0); + } + + function validate(Schema schema) internal view { + // Schema must not be empty + if (schema.isEmpty()) revert Schema_InvalidLength(0); + + // Schema must have no more than 14 dynamic fields + uint256 _numDynamicFields = schema.numDynamicFields(); + if (_numDynamicFields > 14) revert Schema_InvalidLength(_numDynamicFields); + + uint256 _numStaticFields = schema.numStaticFields(); + // Schema must not have more than 28 fields in total + if (_numStaticFields + _numDynamicFields > 28) revert Schema_InvalidLength(_numStaticFields + _numDynamicFields); + + // No static field can be after a dynamic field + uint256 countStaticFields; + uint256 countDynamicFields; + for (uint256 i; i < _numStaticFields + _numDynamicFields; ) { + if (getStaticByteLength(schema.atIndex(i)) > 0) { + // Static field in dynamic part + if (i >= _numStaticFields) revert Schema_StaticTypeAfterDynamicType(); + countStaticFields++; + } else { + // Dynamic field in static part + if (i < _numStaticFields) revert Schema_StaticTypeAfterDynamicType(); + countDynamicFields++; + } + unchecked { + i++; + } + } + + // Number of static fields must match + if (countStaticFields != _numStaticFields) revert Schema_InvalidLength(countStaticFields); + + // Number of dynamic fields must match + if (countDynamicFields != _numDynamicFields) revert Schema_InvalidLength(countDynamicFields); + } + + /** + * Unwrap the schema + */ + function unwrap(Schema schema) internal pure returns (bytes32) { + return Schema.unwrap(schema); + } +} diff --git a/packages/solecs/v2/StoreCore.sol b/packages/solecs/v2/StoreCore.sol index 055178ece7..7536613499 100644 --- a/packages/solecs/v2/StoreCore.sol +++ b/packages/solecs/v2/StoreCore.sol @@ -6,7 +6,9 @@ import { SchemaType, getStaticByteLength, getElementByteLength } from "./Types.s import { Storage } from "./Storage.sol"; import { Memory } from "./Memory.sol"; import { console } from "forge-std/console.sol"; -import "./Buffer.sol"; +import { Schema } from "./Schema.sol"; +import { PackedCounter } from "./PackedCounter.sol"; +import { Buffer, Buffer_ } from "./Buffer.sol"; // TODO // - Make schema a custom data type we can execute methods on, move schema methods to schema library @@ -21,8 +23,6 @@ library StoreCore { bytes32 internal constant SLOT = keccak256("mud.store"); bytes32 internal constant SCHEMA_TABLE = keccak256("mud.store.table.schema"); - error StoreCore_SchemaTooLong(); - error StoreCore_StaticTypeAfterDynamicType(); error StoreCore_TableAlreadyExists(bytes32 table); error StoreCore_NotImplemented(); error StoreCore_InvalidDataLength(uint256 expected, uint256 received); @@ -37,71 +37,43 @@ library StoreCore { * Check if the given table exists */ function hasTable(bytes32 table) internal view returns (bool) { - return getSchema(table) != bytes32(0); + return !getSchema(table).isEmpty(); } /** * Register a new table schema */ - function registerSchema(bytes32 table, SchemaType[] memory schema) internal { - // Verify the schema doesn't exist yet - if (hasTable(table)) { - revert StoreCore_TableAlreadyExists(table); - } - - // Register the schema (validity checks are done in encodeSchema) - _registerSchemaUnchecked(table, encodeSchema(schema)); - } + function registerSchema(bytes32 table, Schema schema) internal { + // Verify the schema is valid + schema.validate(); - /** - * Register a new table schema - */ - function registerSchema(bytes32 table, bytes32 schema) internal { // Verify the schema doesn't exist yet if (hasTable(table)) { revert StoreCore_TableAlreadyExists(table); } - // Verify the schema is not too long - uint256 numFields = _getNumStaticFields(schema) + _getNumDynamicFields(schema); - if (numFields > 28) { - revert StoreCore_SchemaTooLong(); - } - - // Verify all static fields come before the dynamic fields - bool hasDynamicField; - for (uint256 i = 0; i < numFields; i++) { - if (getStaticByteLength(_getSchemaTypeAtIndex(schema, i)) == 0) { - // Flag that we've seen the first dynamic field - hasDynamicField = true; - } else if (hasDynamicField) { - // We've seen a dynamic field, but this field is static - revert StoreCore_StaticTypeAfterDynamicType(); - } - } - - // Verify all dynamic fields come after the static fields + // Register the schema _registerSchemaUnchecked(table, schema); } /** * Register a new table schema without validity checks */ - function _registerSchemaUnchecked(bytes32 table, bytes32 schema) internal { + function _registerSchemaUnchecked(bytes32 table, Schema schema) internal { bytes32[] memory key = new bytes32[](1); key[0] = table; bytes32 location = _getStaticDataLocation(SCHEMA_TABLE, key); - Storage.write(location, schema); + Storage.write(location, schema.unwrap()); } /** * Get the schema for the given table */ - function getSchema(bytes32 table) internal view returns (bytes32 schema) { + function getSchema(bytes32 table) internal view returns (Schema schema) { bytes32[] memory key = new bytes32[](1); key[0] = table; bytes32 location = _getStaticDataLocation(SCHEMA_TABLE, key); - return Storage.read(location); + return Schema.wrap(Storage.read(location)); } /************************************************************************ @@ -116,16 +88,16 @@ library StoreCore { function set( bytes32 table, bytes32[] memory key, - bytes32 encodedDynamicLength, + PackedCounter dynamicLength, bytes memory data ) internal { // verify the value has the correct length for the table (based on the table's schema) // to prevent invalid data from being stored - bytes32 schema = getSchema(table); + Schema schema = getSchema(table); // Verify static data length + dynamic data length matches the given data - uint256 staticLength = _getStaticDataLength(schema); - uint256 expectedLength = staticLength + _decodeDynamicDataTotalLength(encodedDynamicLength); + uint256 staticLength = schema.staticDataLength(); + uint256 expectedLength = staticLength + dynamicLength.total(); if (expectedLength != data.length) { revert StoreCore_InvalidDataLength(expectedLength, data.length); } @@ -138,14 +110,14 @@ library StoreCore { // Store the dynamic data length at the dynamic data length location bytes32 dynamicDataLengthLocation = _getDynamicDataLengthLocation(table, key); - Storage.write(dynamicDataLengthLocation, encodedDynamicLength); + Storage.write(dynamicDataLengthLocation, dynamicLength.unwrap()); // For every dynamic element, slice off the dynamic data and store it at the dynamic location bytes32 dynamicDataLocation; uint256 dynamicDataLength; - for (uint8 i; i < _getNumDynamicFields(schema); ) { + for (uint8 i; i < schema.numDynamicFields(); ) { dynamicDataLocation = _getDynamicDataLocation(table, key, i); - dynamicDataLength = _decodeDynamicDataLengthAtIndex(encodedDynamicLength, i); + dynamicDataLength = dynamicLength.atIndex(i); Storage.write(dynamicDataLocation, 0, memoryPointer, dynamicDataLength); memoryPointer += dynamicDataLength; // move the memory pointer to the start of the next dynamic data unchecked { @@ -167,11 +139,11 @@ library StoreCore { ) internal { // verify the value has the correct length for the table (based on the table's schema) // to prevent invalid data from being stored - bytes32 schema = getSchema(table); + Schema schema = getSchema(table); // Verify the static data length matches the table schema - if (_getStaticDataLength(schema) != data.length) { - revert StoreCore_InvalidDataLength(_getStaticDataLength(schema), data.length); + if (schema.staticDataLength() != data.length) { + revert StoreCore_InvalidDataLength(schema.staticDataLength(), data.length); } // Store the provided value in storage @@ -188,8 +160,8 @@ library StoreCore { uint8 schemaIndex, bytes memory data ) internal { - bytes32 schema = getSchema(table); - if (schemaIndex < _getNumStaticFields(schema)) { + Schema schema = getSchema(table); + if (schemaIndex < schema.numStaticFields()) { _setStaticField(table, key, schema, schemaIndex, data); } else { _setDynamicField(table, key, schema, schemaIndex, data); @@ -199,12 +171,12 @@ library StoreCore { function _setStaticField( bytes32 table, bytes32[] memory key, - bytes32 schema, + Schema schema, uint8 schemaIndex, bytes memory data ) internal { // verify the value has the correct length for the field - SchemaType schemaType = _getSchemaTypeAtIndex(schema, schemaIndex); + SchemaType schemaType = schema.atIndex(schemaIndex); if (getStaticByteLength(schemaType) != data.length) revert StoreCore_InvalidDataLength(getStaticByteLength(schemaType), data.length); @@ -220,11 +192,11 @@ library StoreCore { function _setDynamicField( bytes32 table, bytes32[] memory key, - bytes32 schema, + Schema schema, uint8 schemaIndex, bytes memory data ) internal { - uint8 dynamicSchemaIndex = schemaIndex - _getNumStaticFields(schema); + uint8 dynamicSchemaIndex = schemaIndex - schema.numStaticFields(); // Update the dynamic data length _setDynamicDataLengthAtIndex(table, key, dynamicSchemaIndex, data.length); @@ -248,7 +220,7 @@ library StoreCore { */ function get(bytes32 table, bytes32[] memory key) internal view returns (bytes memory) { // Get schema for this table - bytes32 schema = getSchema(table); + Schema schema = getSchema(table); return get(table, key, schema); } @@ -259,22 +231,20 @@ library StoreCore { function get( bytes32 table, bytes32[] memory key, - bytes32 schema + Schema schema ) internal view returns (bytes memory) { // Get static data length - uint256 staticDataLength = _getStaticDataLength(schema); + uint256 staticDataLength = schema.staticDataLength(); // Load the dynamic data length if there are dynamic fields - bytes32 encodedDynamicLength; - uint256 dynamicDataTotalLength; - uint256 numDynamicFields = _getNumDynamicFields(schema); + PackedCounter dynamicDataLength; + uint256 numDynamicFields = schema.numDynamicFields(); if (numDynamicFields > 0) { - encodedDynamicLength = _loadEncodedDynamicDataLength(table, key); - dynamicDataTotalLength = _decodeDynamicDataTotalLength(encodedDynamicLength); + dynamicDataLength = _loadEncodedDynamicDataLength(table, key); } // Allocate a buffer for the full data (static and dynamic) - Buffer buffer = Buffer_.allocate(uint128(staticDataLength + dynamicDataTotalLength)); + Buffer buffer = Buffer_.allocate(uint128(staticDataLength + dynamicDataLength.total())); // Load the static data from storage and append it to the buffer buffer.append(getStaticData(table, key, schema)); @@ -282,8 +252,7 @@ library StoreCore { // Append dynamic data to the buffer for (uint8 i; i < numDynamicFields; i++) { uint256 dynamicDataLocation = uint256(_getDynamicDataLocation(table, key, i)); - uint256 dynamicDataLengthAtIndex = _decodeDynamicDataLengthAtIndex(encodedDynamicLength, i); - Storage.read(dynamicDataLocation, 0, dynamicDataLengthAtIndex, buffer); + Storage.read(dynamicDataLocation, 0, dynamicDataLength.atIndex(i), buffer); } // Return the buffer as bytes @@ -294,7 +263,7 @@ library StoreCore { * Get full static record for the given table and key tuple (loading schema from storage) */ function getStaticData(bytes32 table, bytes32[] memory key) internal view returns (bytes memory) { - bytes32 schema = getSchema(table); + Schema schema = getSchema(table); return getStaticData(table, key, schema); } @@ -304,11 +273,11 @@ library StoreCore { function getStaticData( bytes32 table, bytes32[] memory key, - bytes32 schema + Schema schema ) internal view returns (bytes memory) { // Load the data from storage bytes32 location = _getStaticDataLocation(table, key); - return Storage.read(location, _getStaticDataLength(schema)); + return Storage.read(location, schema.staticDataLength()); } /** @@ -319,7 +288,7 @@ library StoreCore { bytes32[] memory key, uint8 schemaIndex ) internal view returns (bytes memory) { - bytes32 schema = getSchema(table); + Schema schema = getSchema(table); return getField(table, key, schemaIndex, schema); } @@ -330,9 +299,9 @@ library StoreCore { bytes32 table, bytes32[] memory key, uint8 schemaIndex, - bytes32 schema + Schema schema ) internal view returns (bytes memory) { - if (schemaIndex < _getNumStaticFields(schema)) { + if (schemaIndex < schema.numStaticFields()) { return _getStaticField(table, key, schemaIndex, schema); } else { return _getDynamicField(table, key, schemaIndex, schema); @@ -346,10 +315,10 @@ library StoreCore { bytes32 table, bytes32[] memory key, uint8 schemaIndex, - bytes32 schema + Schema schema ) internal view returns (bytes memory) { // Get the length, storage location and offset of the static field - SchemaType schemaType = _getSchemaTypeAtIndex(schema, schemaIndex); + SchemaType schemaType = schema.atIndex(schemaIndex); uint256 dataLength = getStaticByteLength(schemaType); uint256 location = uint256(_getStaticDataLocation(table, key)); uint256 offset = _getStaticDataOffset(schema, schemaIndex); @@ -365,90 +334,15 @@ library StoreCore { bytes32 table, bytes32[] memory key, uint8 schemaIndex, - bytes32 schema + Schema schema ) internal view returns (bytes memory) { // Get the length and storage location of the dynamic field - uint8 dynamicSchemaIndex = schemaIndex - _getNumStaticFields(schema); - uint256 dataLength = _getDynamicDataLengthAtIndex(table, key, dynamicSchemaIndex); + uint8 dynamicSchemaIndex = schemaIndex - schema.numStaticFields(); bytes32 location = _getDynamicDataLocation(table, key, dynamicSchemaIndex); + uint256 dataLength = _loadEncodedDynamicDataLength(table, key).atIndex(dynamicSchemaIndex); return Storage.read(location, dataLength); } - /************************************************************************ - * - * HELPER FUNCTIONS - * - ************************************************************************/ - - /** - * Encode the given schema into a single bytes32 - * - 2 bytes static length of the schema - * - 1 byte for number of static size fields - * - 1 byte for number of dynamic size fields - * - 28 bytes for 28 schema types (max 14 dynamic fields to we can pack their lengths into 1 word) - */ - function encodeSchema(SchemaType[] memory _schema) internal pure returns (bytes32 schema) { - if (_schema.length > 28) revert StoreCore_SchemaTooLong(); - uint16 length; - uint8 staticFields; - - // Compute the length of the schema and the number of static fields - // and store the schema types in the encoded schema - bool hasDynamicFields; - for (uint256 i = 0; i < _schema.length; ) { - uint16 staticByteLength = uint16(getStaticByteLength(_schema[i])); - - // Increase the static field count if the field is static - if (staticByteLength > 0) { - // Revert if we have seen a dynamic field before, but now we see a static field - if (hasDynamicFields) revert StoreCore_StaticTypeAfterDynamicType(); - staticFields++; - } else { - // Flag that we have seen a dynamic field - hasDynamicFields = true; - } - - length += staticByteLength; - schema = Bytes.setBytes1(schema, i + 4, bytes1(uint8(_schema[i]))); - unchecked { - i++; - } - } - - // Require max 14 dynamic fields - uint8 dynamicFields = uint8(_schema.length) - staticFields; - if (dynamicFields > 14) revert StoreCore_SchemaTooLong(); - - // Store total static length, and number of static and dynamic fields - schema = Bytes.setBytes1(schema, 0, bytes1(bytes2(length))); // upper length byte - schema = Bytes.setBytes1(schema, 1, bytes1(uint8(length))); // lower length byte - schema = Bytes.setBytes1(schema, 2, bytes1(staticFields)); // number of static fields - schema = Bytes.setBytes1(schema, 3, bytes1(dynamicFields)); // number of dynamic fields - } - - /** - * Encode the given dynamic data lengths into a single bytes32 - * - 4 bytes for total length of the dynamic data - * - 2 bytes for each dynamic data length -> max 14 dynamic fields - */ - function encodeDynamicDataLength(uint16[] memory lengths) internal pure returns (bytes32 encodedLength) { - if (lengths.length > 14) revert StoreCore_SchemaTooLong(); - uint32 totalLength; - - // Compute the total length of the dynamic data - // and store the encoded lengths of each item - for (uint256 i; i < lengths.length; ) { - encodedLength = Bytes.setBytes2(encodedLength, 4 + 2 * i, bytes2(lengths[i])); - totalLength += lengths[i]; - unchecked { - i++; - } - } - - // Store total length - encodedLength = Bytes.setBytes4(encodedLength, 0, bytes4(totalLength)); - } - /************************************************************************ * * INTERNAL HELPER FUNCTIONS @@ -469,46 +363,18 @@ library StoreCore { /** * Get storage offset for the given schema and (static length) index */ - function _getStaticDataOffset(bytes32 schema, uint8 schemaIndex) internal pure returns (uint256) { + function _getStaticDataOffset(Schema schema, uint8 schemaIndex) internal pure returns (uint256) { uint256 offset = 0; // skip length for (uint256 i = 0; i < schemaIndex; i++) { - offset += getStaticByteLength(_getSchemaTypeAtIndex(schema, i)); + offset += getStaticByteLength(schema.atIndex(i)); } return offset; } - /** - * Get the number of static fields for the given schema - */ - function _getNumStaticFields(bytes32 schema) internal pure returns (uint8) { - return uint8(Bytes.slice1(schema, 2)); - } - - /** - * Get the length of the static data for the given schema - */ - function _getStaticDataLength(bytes32 schema) internal pure returns (uint256) { - return uint256(uint16(bytes2(schema))); - } - - /** - * Get the type of the data for the given schema at the given index - */ - function _getSchemaTypeAtIndex(bytes32 schema, uint256 index) internal pure returns (SchemaType) { - return SchemaType(uint8(Bytes.slice1(schema, index + 4))); - } - ///////////////////////////////////////////////////////////////////////// // DYNAMIC DATA ///////////////////////////////////////////////////////////////////////// - /** - * Get the number of dynamic length fields for the given schema - */ - function _getNumDynamicFields(bytes32 schema) internal pure returns (uint8) { - return uint8(Bytes.slice1(schema, 3)); - } - /** * Compute the storage location based on table id and index tuple */ @@ -530,61 +396,10 @@ library StoreCore { /** * Get the length of the dynamic data for the given schema and index */ - function _loadEncodedDynamicDataLength(bytes32 table, bytes32[] memory key) internal view returns (bytes32) { + function _loadEncodedDynamicDataLength(bytes32 table, bytes32[] memory key) internal view returns (PackedCounter) { // Load dynamic data length from storage bytes32 dynamicSchemaLengthSlot = _getDynamicDataLengthLocation(table, key); - return Storage.read(dynamicSchemaLengthSlot); - } - - /** - * Decode the full dynamic data length (in bytes) from the given encoded lengths - * (first four bytes of encoded lengths) - */ - function _decodeDynamicDataTotalLength(bytes32 encodedLengths) internal pure returns (uint256) { - return uint256(uint32(bytes4(encodedLengths))); - } - - /** - * Decode the dynamic data length (in bytes) for the given schema and index from the given encoded lengths - * (two bytes per dynamic schema after the first four bytes) - */ - function _decodeDynamicDataLengthAtIndex( - bytes32 encodedLengths, - uint8 dynamicSchemaIndex // schemaIndex - numStaticFields - ) internal pure returns (uint256) { - // Compute dynamic schema index and offset into encoded lengths - uint256 offset = 4 + dynamicSchemaIndex * 2; // (4 bytes total length, 2 bytes per dynamic schema) - - // Return dynamic data length in bytes for the given index - return uint256(uint16(Bytes.slice2(encodedLengths, offset))); - } - - /** - * Get the total length of the dynamic data (in bytes) - * (Note: if the length at a specific index is also required, it is recommended to use _loadEncodedDynamicDataLength explicitly) - */ - function _getDynamicDataTotalLength(bytes32 table, bytes32[] memory key) internal view returns (uint256) { - // Load dynamic data length from storage - bytes32 encodedLengths = _loadEncodedDynamicDataLength(table, key); - - // Return dynamic data length in bytes for the given index - return _decodeDynamicDataTotalLength(encodedLengths); - } - - /** - * Get the length of the dynamic data (in bytes) for the given schema and index - * (Note: if the total length is also required, it is recommended to use _loadEncodedDynamicDataLength explicitly) - */ - function _getDynamicDataLengthAtIndex( - bytes32 table, - bytes32[] memory key, - uint8 dynamicSchemaIndex // schemaIndex - numStaticFields - ) internal view returns (uint256) { - // Load dynamic data length from storage - bytes32 encodedLengths = _loadEncodedDynamicDataLength(table, key); - - // Return dynamic data length in bytes for the given index - return _decodeDynamicDataLengthAtIndex(encodedLengths, dynamicSchemaIndex); + return PackedCounter.wrap(Storage.read(dynamicSchemaLengthSlot)); } /** @@ -598,26 +413,13 @@ library StoreCore { ) internal { // Load dynamic data length from storage bytes32 dynamicSchemaLengthSlot = _getDynamicDataLengthLocation(table, key); - bytes32 encodedLengths = Storage.read(dynamicSchemaLengthSlot); - - // Get current lengths (total and at index) - uint256 totalLength = _decodeDynamicDataTotalLength(encodedLengths); - uint256 currentLengthAtIndex = _decodeDynamicDataLengthAtIndex(encodedLengths, dynamicSchemaIndex); - - // Compute the difference and update the total length - int256 lengthDiff = int256(newLengthAtIndex) - int256(currentLengthAtIndex); - totalLength = uint256(int256(totalLength) + lengthDiff); - - // Compute dynamic schema index and offset into encoded lengths - uint256 offset = 4 + dynamicSchemaIndex * 2; // (4 bytes total length, 2 bytes per dynamic schema) - - // Encode the new lengths - encodedLengths = Bytes.setBytes4(encodedLengths, 0, bytes4(uint32(totalLength))); + PackedCounter encodedLengths = PackedCounter.wrap(Storage.read(dynamicSchemaLengthSlot)); - encodedLengths = Bytes.setBytes2(encodedLengths, offset, bytes2(uint16(newLengthAtIndex))); + // Update the encoded lengths + encodedLengths = encodedLengths.setAtIndex(dynamicSchemaIndex, newLengthAtIndex); // Set the new lengths - Storage.write(dynamicSchemaLengthSlot, encodedLengths); + Storage.write(dynamicSchemaLengthSlot, encodedLengths.unwrap()); } } diff --git a/packages/solecs/v2/StoreSwitch.sol b/packages/solecs/v2/StoreSwitch.sol index bde158bf17..bf922a387a 100644 --- a/packages/solecs/v2/StoreSwitch.sol +++ b/packages/solecs/v2/StoreSwitch.sol @@ -4,6 +4,8 @@ pragma solidity >=0.8.0; import { SchemaType } from "./Types.sol"; import { IStore } from "./IStore.sol"; import { StoreCore } from "./StoreCore.sol"; +import { Schema } from "./Schema.sol"; +import { PackedCounter } from "./PackedCounter.sol"; /** * Call IStore functions on self or msg.sender, depending on whether the call is a delegatecall or regular call. @@ -21,7 +23,7 @@ library StoreSwitch { } } - function registerSchema(bytes32 table, bytes32 schema) internal { + function registerSchema(bytes32 table, Schema schema) internal { if (isDelegateCall()) { StoreCore.registerSchema(table, schema); } else { @@ -29,7 +31,7 @@ library StoreSwitch { } } - function getSchema(bytes32 table) internal view returns (bytes32 schema) { + function getSchema(bytes32 table) internal view returns (Schema schema) { if (isDelegateCall()) { schema = StoreCore.getSchema(table); } else { @@ -40,7 +42,7 @@ library StoreSwitch { function set( bytes32 table, bytes32[] memory key, - bytes32 encodedDynamicLength, + PackedCounter encodedDynamicLength, bytes memory data ) internal { if (isDelegateCall()) { diff --git a/packages/solecs/v2/StoreView.sol b/packages/solecs/v2/StoreView.sol index 5ecdc0f21e..9bb060df1a 100644 --- a/packages/solecs/v2/StoreView.sol +++ b/packages/solecs/v2/StoreView.sol @@ -4,23 +4,25 @@ pragma solidity >=0.8.0; import { SchemaType } from "./Types.sol"; import { IStore } from "./IStore.sol"; import { StoreCore } from "./StoreCore.sol"; +import { Schema } from "./Schema.sol"; +import { PackedCounter } from "./PackedCounter.sol"; // Not abstract, so that it can be used as a base contract for testing and wherever write access is not needed contract StoreView is IStore { error Store_BaseContractNotImplemented(); - function getSchema(bytes32 table) public view virtual returns (bytes32 schema) { + function getSchema(bytes32 table) public view virtual returns (Schema schema) { schema = StoreCore.getSchema(table); } - function registerSchema(bytes32, bytes32) public virtual { + function registerSchema(bytes32, Schema) public virtual { revert Store_BaseContractNotImplemented(); } function set( bytes32, bytes32[] memory, - bytes32, + PackedCounter, bytes memory ) public virtual { revert Store_BaseContractNotImplemented(); diff --git a/packages/solecs/v2/World.sol b/packages/solecs/v2/World.sol index 4d23b33d5e..8395e05a44 100644 --- a/packages/solecs/v2/World.sol +++ b/packages/solecs/v2/World.sol @@ -7,6 +7,8 @@ import { StoreView } from "./StoreView.sol"; import { SchemaType, ExecutionMode } from "./Types.sol"; import { RouteTable, Route, id as RouteId } from "./tables/RouteTable.sol"; import { Bytes } from "./Bytes.sol"; +import { PackedCounter } from "./PackedCounter.sol"; +import { Schema } from "./Schema.sol"; /** * TODO: add access control @@ -18,14 +20,14 @@ contract World is StoreView { registerSchema(RouteId, RouteTable.getSchema()); } - function registerSchema(bytes32 table, bytes32 schema) public override { + function registerSchema(bytes32 table, Schema schema) public override { StoreCore.registerSchema(table, schema); } function set( bytes32 table, bytes32[] memory key, - bytes32 encodedDynamicLength, + PackedCounter encodedDynamicLength, bytes memory data ) public override { StoreCore.set(table, key, encodedDynamicLength, data); diff --git a/packages/solecs/v2/tables/RouteTable.sol b/packages/solecs/v2/tables/RouteTable.sol index 79f1979808..6aff26442e 100644 --- a/packages/solecs/v2/tables/RouteTable.sol +++ b/packages/solecs/v2/tables/RouteTable.sol @@ -7,6 +7,7 @@ import { StoreSwitch } from "../StoreSwitch.sol"; import { StoreCore } from "../StoreCore.sol"; import { SchemaType } from "../Types.sol"; import { Bytes } from "../Bytes.sol"; +import { Schema, Schema_ } from "../Schema.sol"; // -- User defined schema and id -- @@ -23,16 +24,8 @@ struct Route { library RouteTable { /** Get the table's schema */ - function getSchema() internal pure returns (bytes32 schema) { - schema = bytes32( - //
- bytes.concat( - bytes2(uint16(25)), - bytes1(uint8(SchemaType.Address)), - bytes1(uint8(SchemaType.Bytes4)), - bytes1(uint8(SchemaType.Uint8)) - ) - ); + function getSchema() internal pure returns (Schema schema) { + schema = Schema_.encode(SchemaType.Address, SchemaType.Bytes4, SchemaType.Uint8); } /** Register the table's schema */ diff --git a/packages/solecs/v2/tables/Vector2Table.sol b/packages/solecs/v2/tables/Vector2Table.sol index 641a6925ff..8c1cb649fd 100644 --- a/packages/solecs/v2/tables/Vector2Table.sol +++ b/packages/solecs/v2/tables/Vector2Table.sol @@ -7,6 +7,7 @@ import { StoreSwitch } from "../StoreSwitch.sol"; import { StoreCore } from "../StoreCore.sol"; import { SchemaType } from "../Types.sol"; import { Bytes } from "../Bytes.sol"; +import { Schema, Schema_ } from "../Schema.sol"; // -- User defined schema and id -- @@ -22,11 +23,8 @@ struct Vector2 { library Vector2Table { /** Get the table's schema */ - function getSchema() internal pure returns (bytes32 schema) { - schema = bytes32( - // - bytes.concat(bytes2(uint16(8)), bytes1(uint8(SchemaType.Uint32)), bytes1(uint8(SchemaType.Uint32))) - ); + function getSchema() internal pure returns (Schema schema) { + schema = Schema_.encode(SchemaType.Uint32, SchemaType.Uint32); } /** Register the table's schema */ diff --git a/packages/solecs/v2/test/PackedCounter.t.sol b/packages/solecs/v2/test/PackedCounter.t.sol new file mode 100644 index 0000000000..b090830965 --- /dev/null +++ b/packages/solecs/v2/test/PackedCounter.t.sol @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import { console } from "forge-std/console.sol"; +import { DSTestPlus } from "solmate/test/utils/DSTestPlus.sol"; +import { PackedCounter, PackedCounter_ } from "../PackedCounter.sol"; + +contract PackedCounterTest is DSTestPlus { + function testTotal() public { + uint16[] memory counters = new uint16[](4); + counters[0] = 1; + counters[1] = 2; + counters[2] = 3; + counters[3] = 4; + + uint256 gas = gasleft(); + PackedCounter packedCounter = PackedCounter_.pack(counters); + + gas = gas - gasleft(); + console.log("gas used (pack): %s", gas); + + gas = gasleft(); + packedCounter.total(); + gas = gas - gasleft(); + console.log("gas used (total): %s", gas); + + assertEq(packedCounter.total(), 10); + } + + function testAtIndex() public { + uint16[] memory counters = new uint16[](4); + counters[0] = 1; + counters[1] = 2; + counters[2] = 3; + counters[3] = 4; + + uint256 gas = gasleft(); + PackedCounter packedCounter = PackedCounter_.pack(counters); + + gas = gas - gasleft(); + console.log("gas used (pack): %s", gas); + + gas = gasleft(); + packedCounter.atIndex(3); + gas = gas - gasleft(); + console.log("gas used (at index): %s", gas); + + assertEq(packedCounter.atIndex(0), 1); + assertEq(packedCounter.atIndex(1), 2); + assertEq(packedCounter.atIndex(2), 3); + assertEq(packedCounter.atIndex(3), 4); + } + + function testSetAtIndex() public { + uint16[] memory counters = new uint16[](4); + counters[0] = 1; + counters[1] = 2; + counters[2] = 3; + counters[3] = 4; + + PackedCounter packedCounter = PackedCounter_.pack(counters); + + uint256 gas = gasleft(); + packedCounter = packedCounter.setAtIndex(2, 5); + gas = gas - gasleft(); + console.log("gas used (at index): %s", gas); + + assertEq(packedCounter.atIndex(0), 1); + assertEq(packedCounter.atIndex(1), 2); + assertEq(packedCounter.atIndex(2), 5); + assertEq(packedCounter.atIndex(3), 4); + assertEq(packedCounter.total(), 12); + } +} diff --git a/packages/solecs/v2/test/RouteTable.t.sol b/packages/solecs/v2/test/RouteTable.t.sol index d708457a58..24d2ae208c 100644 --- a/packages/solecs/v2/test/RouteTable.t.sol +++ b/packages/solecs/v2/test/RouteTable.t.sol @@ -7,6 +7,7 @@ import { RouteTable, id as RouteTableId, Route } from "../tables/RouteTable.sol" import { StoreCore } from "../StoreCore.sol"; import { SchemaType } from "../Types.sol"; import { StoreView } from "../StoreView.sol"; +import { Schema } from "../Schema.sol"; contract RouteTableTest is DSTestPlus, StoreView { function testRegisterAndGetSchema() public { @@ -15,8 +16,8 @@ contract RouteTableTest is DSTestPlus, StoreView { gas = gas - gasleft(); console.log("gas used: %s", gas); - bytes32 registeredSchema = StoreCore.getSchema(RouteTableId); - bytes32 declaredSchema = RouteTable.getSchema(); + Schema registeredSchema = StoreCore.getSchema(RouteTableId); + Schema declaredSchema = RouteTable.getSchema(); assertEq(keccak256(abi.encode(registeredSchema)), keccak256(abi.encode(declaredSchema))); } diff --git a/packages/solecs/v2/test/Schema.t.sol b/packages/solecs/v2/test/Schema.t.sol new file mode 100644 index 0000000000..49f205539e --- /dev/null +++ b/packages/solecs/v2/test/Schema.t.sol @@ -0,0 +1,241 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import { console } from "forge-std/console.sol"; +import { DSTestPlus } from "solmate/test/utils/DSTestPlus.sol"; +import { Schema, Schema_ } from "../Schema.sol"; +import { SchemaType } from "../Types.sol"; + +contract SchemaTest is DSTestPlus { + function testEncodeDecodeSchema() public { + uint256 gas = gasleft(); + Schema schema = Schema_.encode( + SchemaType.Uint8, // 1 byte + SchemaType.Uint16, // 2 bytes + SchemaType.Uint32, // 4 bytes + SchemaType.Uint128, // 16 bytes + SchemaType.Uint256, // 32 bytes + SchemaType.Uint32Array // 0 bytes (because it's dynamic) + ); + gas = gas - gasleft(); + console.log("gas used (encode): %s", gas); + + gas = gasleft(); + SchemaType schemaType1 = schema.atIndex(0); + gas = gas - gasleft(); + console.log("gas used (decode): %s", gas); + + assertEq(uint8(schemaType1), uint8(SchemaType.Uint8)); + assertEq(uint8(schema.atIndex(1)), uint8(SchemaType.Uint16)); + assertEq(uint8(schema.atIndex(2)), uint8(SchemaType.Uint32)); + assertEq(uint8(schema.atIndex(3)), uint8(SchemaType.Uint128)); + assertEq(uint8(schema.atIndex(4)), uint8(SchemaType.Uint256)); + assertEq(uint8(schema.atIndex(5)), uint8(SchemaType.Uint32Array)); + } + + function testFailInvalidSchemaStaticAfterDynamic() public pure { + Schema_.encode(SchemaType.Uint8, SchemaType.Uint32Array, SchemaType.Uint16); + } + + function testEncodeMaxValidLength() public { + SchemaType[] memory schema = new SchemaType[](28); + schema[0] = SchemaType.Uint256; + schema[1] = SchemaType.Uint256; + schema[2] = SchemaType.Uint256; + schema[3] = SchemaType.Uint256; + schema[4] = SchemaType.Uint256; + schema[5] = SchemaType.Uint256; + schema[6] = SchemaType.Uint256; + schema[7] = SchemaType.Uint256; + schema[8] = SchemaType.Uint256; + schema[9] = SchemaType.Uint256; + schema[10] = SchemaType.Uint256; + schema[11] = SchemaType.Uint256; + schema[12] = SchemaType.Uint256; + schema[13] = SchemaType.Uint256; + schema[14] = SchemaType.Uint32Array; + schema[15] = SchemaType.Uint32Array; + schema[16] = SchemaType.Uint32Array; + schema[17] = SchemaType.Uint32Array; + schema[18] = SchemaType.Uint32Array; + schema[19] = SchemaType.Uint32Array; + schema[20] = SchemaType.Uint32Array; + schema[21] = SchemaType.Uint32Array; + schema[22] = SchemaType.Uint32Array; + schema[23] = SchemaType.Uint32Array; + schema[24] = SchemaType.Uint32Array; + schema[25] = SchemaType.Uint32Array; + schema[26] = SchemaType.Uint32Array; + schema[27] = SchemaType.Uint32Array; + Schema encodedSchema = Schema_.encode(schema); + + assertEq(encodedSchema.numStaticFields() + encodedSchema.numDynamicFields(), 28); + } + + function testFailEncodeTooLong() public pure { + SchemaType[] memory schema = new SchemaType[](29); + schema[0] = SchemaType.Uint256; + schema[1] = SchemaType.Uint256; + schema[2] = SchemaType.Uint256; + schema[3] = SchemaType.Uint256; + schema[4] = SchemaType.Uint256; + schema[5] = SchemaType.Uint256; + schema[6] = SchemaType.Uint256; + schema[7] = SchemaType.Uint256; + schema[8] = SchemaType.Uint256; + schema[9] = SchemaType.Uint256; + schema[10] = SchemaType.Uint256; + schema[11] = SchemaType.Uint256; + schema[12] = SchemaType.Uint256; + schema[13] = SchemaType.Uint256; + schema[14] = SchemaType.Uint256; + schema[15] = SchemaType.Uint256; + schema[16] = SchemaType.Uint256; + schema[17] = SchemaType.Uint32Array; + schema[18] = SchemaType.Uint32Array; + schema[19] = SchemaType.Uint32Array; + schema[20] = SchemaType.Uint32Array; + schema[21] = SchemaType.Uint32Array; + schema[22] = SchemaType.Uint32Array; + schema[23] = SchemaType.Uint32Array; + schema[24] = SchemaType.Uint32Array; + schema[25] = SchemaType.Uint32Array; + schema[26] = SchemaType.Uint32Array; + schema[27] = SchemaType.Uint32Array; + schema[28] = SchemaType.Uint32Array; + Schema_.encode(schema); + } + + function testEncodeMaxValidDynamic() public { + SchemaType[] memory schema = new SchemaType[](14); + schema[0] = SchemaType.Uint32Array; + schema[1] = SchemaType.Uint32Array; + schema[2] = SchemaType.Uint32Array; + schema[3] = SchemaType.Uint32Array; + schema[4] = SchemaType.Uint32Array; + schema[5] = SchemaType.Uint32Array; + schema[6] = SchemaType.Uint32Array; + schema[7] = SchemaType.Uint32Array; + schema[8] = SchemaType.Uint32Array; + schema[9] = SchemaType.Uint32Array; + schema[10] = SchemaType.Uint32Array; + schema[11] = SchemaType.Uint32Array; + schema[12] = SchemaType.Uint32Array; + schema[13] = SchemaType.Uint32Array; + Schema encodedSchema = Schema_.encode(schema); + + assertEq(encodedSchema.numDynamicFields(), 14); + } + + function testFailEncodeTooManyDynamic() public pure { + SchemaType[] memory schema = new SchemaType[](15); + schema[0] = SchemaType.Uint32Array; + schema[1] = SchemaType.Uint32Array; + schema[2] = SchemaType.Uint32Array; + schema[3] = SchemaType.Uint32Array; + schema[4] = SchemaType.Uint32Array; + schema[5] = SchemaType.Uint32Array; + schema[6] = SchemaType.Uint32Array; + schema[7] = SchemaType.Uint32Array; + schema[8] = SchemaType.Uint32Array; + schema[9] = SchemaType.Uint32Array; + schema[10] = SchemaType.Uint32Array; + schema[11] = SchemaType.Uint32Array; + schema[12] = SchemaType.Uint32Array; + schema[13] = SchemaType.Uint32Array; + schema[14] = SchemaType.Uint32Array; + Schema_.encode(schema); + } + + function testGetStaticSchemaLength() public { + Schema schema = Schema_.encode( + SchemaType.Uint8, // 1 byte + SchemaType.Uint16, // 2 bytes + SchemaType.Uint32, // 4 bytes + SchemaType.Uint128, // 16 bytes + SchemaType.Uint256, // 32 bytes + SchemaType.Uint32Array // 0 bytes (because it's dynamic) + ); + + uint256 gas = gasleft(); + uint256 length = schema.staticDataLength(); + gas = gas - gasleft(); + console.log("gas used: %s", gas); + + assertEq(length, 55); + } + + function testGetNumStaticFields() public { + Schema schema = Schema_.encode( + SchemaType.Uint8, // 1 byte + SchemaType.Uint16, // 2 bytes + SchemaType.Uint32, // 4 bytes + SchemaType.Uint128, // 16 bytes + SchemaType.Uint256, // 32 bytes + SchemaType.Uint32Array // 0 bytes (because it's dynamic) + ); + + uint256 gas = gasleft(); + uint256 num = schema.numStaticFields(); + gas = gas - gasleft(); + console.log("gas used: %s", gas); + + assertEq(num, 5); + } + + function testGetNumDynamicFields() public { + Schema schema = Schema_.encode( + SchemaType.Uint8, // 1 byte + SchemaType.Uint16, // 2 bytes + SchemaType.Uint32, // 4 bytes + SchemaType.Uint128, // 16 bytes + SchemaType.Uint256, // 32 bytes + SchemaType.Uint32Array // 0 bytes (because it's dynamic) + ); + + uint256 gas = gasleft(); + uint256 num = schema.numDynamicFields(); + gas = gas - gasleft(); + console.log("gas used: %s", gas); + + assertEq(num, 1); + } + + function testValidate() public view { + SchemaType[] memory schema = new SchemaType[](28); + schema[0] = SchemaType.Uint256; + schema[1] = SchemaType.Uint256; + schema[2] = SchemaType.Uint256; + schema[3] = SchemaType.Uint256; + schema[4] = SchemaType.Uint256; + schema[5] = SchemaType.Uint256; + schema[6] = SchemaType.Uint256; + schema[7] = SchemaType.Uint256; + schema[8] = SchemaType.Uint256; + schema[9] = SchemaType.Uint256; + schema[10] = SchemaType.Uint256; + schema[11] = SchemaType.Uint256; + schema[12] = SchemaType.Uint256; + schema[13] = SchemaType.Uint256; + schema[14] = SchemaType.Uint32Array; + schema[15] = SchemaType.Uint32Array; + schema[16] = SchemaType.Uint32Array; + schema[17] = SchemaType.Uint32Array; + schema[18] = SchemaType.Uint32Array; + schema[19] = SchemaType.Uint32Array; + schema[20] = SchemaType.Uint32Array; + schema[21] = SchemaType.Uint32Array; + schema[22] = SchemaType.Uint32Array; + schema[23] = SchemaType.Uint32Array; + schema[24] = SchemaType.Uint32Array; + schema[25] = SchemaType.Uint32Array; + schema[26] = SchemaType.Uint32Array; + schema[27] = SchemaType.Uint32Array; + Schema encodedSchema = Schema_.encode(schema); + encodedSchema.validate(); + } + + function testFailValidate() public view { + Schema.wrap(keccak256("some invalid schema")).validate(); + } +} diff --git a/packages/solecs/v2/test/StoreCore.t.sol b/packages/solecs/v2/test/StoreCore.t.sol index 0a026009c9..f25706c3f1 100644 --- a/packages/solecs/v2/test/StoreCore.t.sol +++ b/packages/solecs/v2/test/StoreCore.t.sol @@ -10,7 +10,9 @@ import { SchemaType } from "../Types.sol"; import { Storage } from "../Storage.sol"; import { Memory } from "../Memory.sol"; import { Cast } from "../Cast.sol"; -import "../Buffer.sol"; +import { Buffer, Buffer_ } from "../Buffer.sol"; +import { Schema, Schema_ } from "../Schema.sol"; +import { PackedCounter, PackedCounter_ } from "../PackedCounter.sol"; struct TestStruct { uint128 firstData; @@ -19,66 +21,11 @@ struct TestStruct { } contract StoreCoreTest is DSTestPlus { - TestStruct testStruct; - mapping(uint256 => bytes) testMapping; - - function testGetStaticDataLength() public { - bytes32 schema = bytes32( - bytes.concat(bytes2(uint16(3)), bytes1(uint8(SchemaType.Uint8)), bytes1(uint8(SchemaType.Uint16))) - ); - - uint256 gas = gasleft(); - uint256 length = StoreCore._getStaticDataLength(schema); - gas = gas - gasleft(); - console.log("gas used: %s", gas); - - assertEq(length, 3); - } - - function testEncodeDecodeSchema() public { - SchemaType[] memory schema = new SchemaType[](6); - schema[0] = SchemaType.Uint8; // 1 byte - schema[1] = SchemaType.Uint16; // 2 bytes - schema[2] = SchemaType.Uint32; // 4 bytes - schema[3] = SchemaType.Uint128; // 4 bytes - schema[4] = SchemaType.Uint256; // 4 bytes - schema[5] = SchemaType.Uint32Array; // 0 bytes (because it's dynamic) - - uint256 gas = gasleft(); - bytes32 encodedSchema = StoreCore.encodeSchema(schema); - gas = gas - gasleft(); - console.log("gas used (encode): %s", gas); - - gas = gasleft(); - uint256 length = StoreCore._getStaticDataLength(encodedSchema); - gas = gas - gasleft(); - console.log("gas used (get length): %s", gas); - - gas = gasleft(); - SchemaType schemaType1 = StoreCore._getSchemaTypeAtIndex(encodedSchema, 0); - gas = gas - gasleft(); - console.log("gas used (decode): %s", gas); - - assertEq(length, 55); - assertEq(uint8(schemaType1), uint8(SchemaType.Uint8)); - assertEq(uint8(StoreCore._getSchemaTypeAtIndex(encodedSchema, 1)), uint8(SchemaType.Uint16)); - assertEq(uint8(StoreCore._getSchemaTypeAtIndex(encodedSchema, 2)), uint8(SchemaType.Uint32)); - assertEq(uint8(StoreCore._getSchemaTypeAtIndex(encodedSchema, 3)), uint8(SchemaType.Uint128)); - assertEq(uint8(StoreCore._getSchemaTypeAtIndex(encodedSchema, 4)), uint8(SchemaType.Uint256)); - assertEq(StoreCore._getNumStaticFields(encodedSchema), 5); - assertEq(StoreCore._getNumDynamicFields(encodedSchema), 1); - } + TestStruct private testStruct; + mapping(uint256 => bytes) private testMapping; function testRegisterAndGetSchema() public { - bytes32 schema = bytes32( - bytes.concat( - bytes2(uint16(6)), - bytes1(uint8(SchemaType.Uint8)), - bytes1(uint8(SchemaType.Uint16)), - bytes1(uint8(SchemaType.Uint8)), - bytes1(uint8(SchemaType.Uint16)) - ) - ); + Schema schema = Schema_.encode(SchemaType.Uint8, SchemaType.Uint16, SchemaType.Uint8, SchemaType.Uint16); bytes32 table = keccak256("some.table"); uint256 gas = gasleft(); @@ -87,78 +34,19 @@ contract StoreCoreTest is DSTestPlus { console.log("gas used (register): %s", gas); gas = gasleft(); - bytes memory loadedSchema = bytes.concat(StoreCore.getSchema(table)); + Schema loadedSchema = StoreCore.getSchema(table); gas = gas - gasleft(); console.log("gas used (get schema, warm): %s", gas); - assertEq(loadedSchema.length, schema.length); - assertEq(uint8(Bytes.slice1(loadedSchema, 2)), uint8(SchemaType.Uint8)); - assertEq(uint8(Bytes.slice1(loadedSchema, 3)), uint8(SchemaType.Uint16)); - assertEq(uint8(Bytes.slice1(loadedSchema, 4)), uint8(SchemaType.Uint8)); - assertEq(uint8(Bytes.slice1(loadedSchema, 5)), uint8(SchemaType.Uint16)); - } - - function testFailInvalidSchemaStaticAfterDynamic() public { - bytes32 table = keccak256("table"); - SchemaType[] memory schema = new SchemaType[](3); - schema[0] = SchemaType.Uint8; - schema[1] = SchemaType.Uint32Array; - schema[2] = SchemaType.Uint16; - StoreCore.registerSchema(table, schema); - } - - function testRegisterSchemaMaxDynamic() public { - bytes32 table = keccak256("table"); - SchemaType[] memory schema = new SchemaType[](14); - schema[0] = SchemaType.Uint32Array; - schema[1] = SchemaType.Uint32Array; - schema[2] = SchemaType.Uint32Array; - schema[3] = SchemaType.Uint32Array; - schema[4] = SchemaType.Uint32Array; - schema[5] = SchemaType.Uint32Array; - schema[6] = SchemaType.Uint32Array; - schema[7] = SchemaType.Uint32Array; - schema[8] = SchemaType.Uint32Array; - schema[9] = SchemaType.Uint32Array; - schema[10] = SchemaType.Uint32Array; - schema[11] = SchemaType.Uint32Array; - schema[12] = SchemaType.Uint32Array; - schema[13] = SchemaType.Uint32Array; - StoreCore.registerSchema(table, schema); + assertEq(schema.unwrap(), loadedSchema.unwrap()); } - function testFailInvalidSchemaTooManyDynamic() public { - bytes32 table = keccak256("table"); - SchemaType[] memory schema = new SchemaType[](15); - schema[0] = SchemaType.Uint32Array; - schema[1] = SchemaType.Uint32Array; - schema[2] = SchemaType.Uint32Array; - schema[3] = SchemaType.Uint32Array; - schema[4] = SchemaType.Uint32Array; - schema[5] = SchemaType.Uint32Array; - schema[6] = SchemaType.Uint32Array; - schema[7] = SchemaType.Uint32Array; - schema[8] = SchemaType.Uint32Array; - schema[9] = SchemaType.Uint32Array; - schema[10] = SchemaType.Uint32Array; - schema[11] = SchemaType.Uint32Array; - schema[12] = SchemaType.Uint32Array; - schema[13] = SchemaType.Uint32Array; - schema[14] = SchemaType.Uint32Array; - StoreCore.registerSchema(table, schema); + function testFailRegisterInvalidSchema() public { + StoreCore.registerSchema(keccak256("table"), Schema.wrap(keccak256("random bytes as schema"))); } function testHasSchema() public { - bytes32 schema = bytes32( - bytes.concat( - bytes2(uint16(6)), - bytes1(uint8(SchemaType.Uint8)), - bytes1(uint8(SchemaType.Uint16)), - bytes1(uint8(SchemaType.Uint8)), - bytes1(uint8(SchemaType.Uint16)) - ) - ); - + Schema schema = Schema_.encode(SchemaType.Uint8, SchemaType.Uint16, SchemaType.Uint8, SchemaType.Uint16); bytes32 table = keccak256("some.table"); bytes32 table2 = keccak256("other.table"); StoreCore.registerSchema(table, schema); @@ -177,46 +65,16 @@ contract StoreCoreTest is DSTestPlus { assertFalse(StoreCore.hasTable(table2)); } - function testEncodeAndDecodeDynamicLength() public { - uint16[] memory lengths = new uint16[](4); - lengths[0] = 1; - lengths[1] = 2; - lengths[2] = 3; - lengths[3] = 4; - - uint256 gas = gasleft(); - bytes32 encodedLengths = StoreCore.encodeDynamicDataLength(lengths); - gas = gas - gasleft(); - console.log("gas used (encode): %s", gas); - - gas = gasleft(); - StoreCore._decodeDynamicDataLengthAtIndex(encodedLengths, 3); - gas = gas - gasleft(); - console.log("gas used (decode index): %s", gas); - - gas = gasleft(); - StoreCore._decodeDynamicDataTotalLength(encodedLengths); - gas = gas - gasleft(); - console.log("gas used (decode total): %s", gas); - - assertEq(StoreCore._decodeDynamicDataLengthAtIndex(encodedLengths, 0), 1); - assertEq(StoreCore._decodeDynamicDataLengthAtIndex(encodedLengths, 1), 2); - assertEq(StoreCore._decodeDynamicDataLengthAtIndex(encodedLengths, 2), 3); - assertEq(StoreCore._decodeDynamicDataLengthAtIndex(encodedLengths, 3), 4); - assertEq(StoreCore._decodeDynamicDataTotalLength(encodedLengths), 10); - } - function testSetAndGetDynamicDataLength() public { bytes32 table = keccak256("some.table"); - SchemaType[] memory _schema = new SchemaType[](6); - _schema[0] = SchemaType.Uint8; // 1 byte - _schema[1] = SchemaType.Uint16; // 2 bytes - _schema[2] = SchemaType.Uint32; // 4 bytes - _schema[3] = SchemaType.Uint32Array; // 0 bytes (because it's dynamic) - _schema[4] = SchemaType.Uint32Array; // 0 bytes (because it's dynamic) - - bytes32 schema = StoreCore.encodeSchema(_schema); + Schema schema = Schema_.encode( + SchemaType.Uint8, + SchemaType.Uint16, + SchemaType.Uint32, + SchemaType.Uint32Array, + SchemaType.Uint32Array + ); // Register schema StoreCore.registerSchema(table, schema); @@ -231,9 +89,10 @@ contract StoreCoreTest is DSTestPlus { gas = gas - gasleft(); console.log("gas used (set length): %s", gas); - assertEq(StoreCore._getDynamicDataLengthAtIndex(table, key, 0), 10); - assertEq(StoreCore._getDynamicDataLengthAtIndex(table, key, 1), 0); - assertEq(StoreCore._getDynamicDataTotalLength(table, key), 10); + PackedCounter encodedLength = StoreCore._loadEncodedDynamicDataLength(table, key); + assertEq(encodedLength.atIndex(0), 10); + assertEq(encodedLength.atIndex(1), 0); + assertEq(encodedLength.total(), 10); // Set dynamic data length of dynamic index 1 gas = gasleft(); @@ -241,9 +100,10 @@ contract StoreCoreTest is DSTestPlus { gas = gas - gasleft(); console.log("gas used (set length): %s", gas); - assertEq(StoreCore._getDynamicDataLengthAtIndex(table, key, 0), 10); - assertEq(StoreCore._getDynamicDataLengthAtIndex(table, key, 1), 99); - assertEq(StoreCore._getDynamicDataTotalLength(table, key), 109); + encodedLength = StoreCore._loadEncodedDynamicDataLength(table, key); + assertEq(encodedLength.atIndex(0), 10); + assertEq(encodedLength.atIndex(1), 99); + assertEq(encodedLength.total(), 109); // Reduce dynamic data length of dynamic index 0 again gas = gasleft(); @@ -251,27 +111,15 @@ contract StoreCoreTest is DSTestPlus { gas = gas - gasleft(); console.log("gas used (set length): %s", gas); - assertEq(StoreCore._getDynamicDataLengthAtIndex(table, key, 0), 5); - assertEq(StoreCore._getDynamicDataLengthAtIndex(table, key, 1), 99); - assertEq(StoreCore._getDynamicDataTotalLength(table, key), 104); - - gas = gasleft(); - StoreCore._getDynamicDataLengthAtIndex(table, key, 0); - gas = gas - gasleft(); - console.log("gas used (get length at index): %s", gas); + encodedLength = StoreCore._loadEncodedDynamicDataLength(table, key); + assertEq(encodedLength.atIndex(0), 5); + assertEq(encodedLength.atIndex(1), 99); + assertEq(encodedLength.total(), 104); } function testSetAndGetStaticData() public { // Register table's schema - bytes32 schema = bytes32( - bytes.concat( - bytes2(uint16(6)), - bytes1(uint8(SchemaType.Uint8)), - bytes1(uint8(SchemaType.Uint16)), - bytes1(uint8(SchemaType.Uint8)), - bytes1(uint8(SchemaType.Uint16)) - ) - ); + Schema schema = Schema_.encode(SchemaType.Uint8, SchemaType.Uint16, SchemaType.Uint8, SchemaType.Uint16); bytes32 table = keccak256("some.table"); StoreCore.registerSchema(table, schema); @@ -298,16 +146,7 @@ contract StoreCoreTest is DSTestPlus { function testFailSetAndGetStaticData() public { // Register table's schema - bytes32 schema = bytes32( - bytes.concat( - bytes2(uint16(6)), - bytes1(uint8(SchemaType.Uint8)), - bytes1(uint8(SchemaType.Uint16)), - bytes1(uint8(SchemaType.Uint8)), - bytes1(uint8(SchemaType.Uint16)) - ) - ); - + Schema schema = Schema_.encode(SchemaType.Uint8, SchemaType.Uint16, SchemaType.Uint8, SchemaType.Uint16); bytes32 table = keccak256("some.table"); StoreCore.registerSchema(table, schema); @@ -326,12 +165,7 @@ contract StoreCoreTest is DSTestPlus { function testSetAndGetStaticDataSpanningWords() public { // Register table's schema - SchemaType[] memory schemaTypes = new SchemaType[](2); - schemaTypes[0] = SchemaType.Uint128; - schemaTypes[1] = SchemaType.Uint256; - - bytes32 schema = StoreCore.encodeSchema(schemaTypes); - + Schema schema = Schema_.encode(SchemaType.Uint128, SchemaType.Uint256); bytes32 table = keccak256("some.table"); StoreCore.registerSchema(table, schema); @@ -363,11 +197,7 @@ contract StoreCoreTest is DSTestPlus { { // Register table's schema - SchemaType[] memory schemaTypes = new SchemaType[](3); - schemaTypes[0] = SchemaType.Uint128; - schemaTypes[1] = SchemaType.Uint32Array; - schemaTypes[2] = SchemaType.Uint32Array; - bytes32 schema = StoreCore.encodeSchema(schemaTypes); + Schema schema = Schema_.encode(SchemaType.Uint128, SchemaType.Uint32Array, SchemaType.Uint32Array); StoreCore.registerSchema(table, schema); } @@ -390,12 +220,12 @@ contract StoreCoreTest is DSTestPlus { thirdDataBytes = Bytes.from(thirdData); } - bytes32 encodedDynamicLength; + PackedCounter encodedDynamicLength; { uint16[] memory dynamicLengths = new uint16[](2); dynamicLengths[0] = uint16(secondDataBytes.length); dynamicLengths[1] = uint16(thirdDataBytes.length); - encodedDynamicLength = StoreCore.encodeDynamicDataLength(dynamicLengths); + encodedDynamicLength = PackedCounter_.pack(dynamicLengths); } // Concat data @@ -445,12 +275,12 @@ contract StoreCoreTest is DSTestPlus { { // Register table's schema - SchemaType[] memory schemaTypes = new SchemaType[](4); - schemaTypes[0] = SchemaType.Uint128; - schemaTypes[1] = SchemaType.Uint256; - schemaTypes[2] = SchemaType.Uint32Array; - schemaTypes[3] = SchemaType.Uint32Array; - bytes32 schema = StoreCore.encodeSchema(schemaTypes); + Schema schema = Schema_.encode( + SchemaType.Uint128, + SchemaType.Uint256, + SchemaType.Uint32Array, + SchemaType.Uint32Array + ); StoreCore.registerSchema(table, schema); } diff --git a/packages/solecs/v2/test/Vector2Table.t.sol b/packages/solecs/v2/test/Vector2Table.t.sol index 29a84f0e86..ed045b96ab 100644 --- a/packages/solecs/v2/test/Vector2Table.t.sol +++ b/packages/solecs/v2/test/Vector2Table.t.sol @@ -7,6 +7,7 @@ import { Vector2Table, id as Vector2Id, Vector2 } from "../tables/Vector2Table.s import { StoreCore } from "../StoreCore.sol"; import { SchemaType } from "../Types.sol"; import { StoreView } from "../StoreView.sol"; +import { Schema } from "../Schema.sol"; contract Vector2TableTest is DSTestPlus, StoreView { function testRegisterAndGetSchema() public { @@ -15,8 +16,8 @@ contract Vector2TableTest is DSTestPlus, StoreView { gas = gas - gasleft(); console.log("gas used: %s", gas); - bytes32 registeredSchema = StoreCore.getSchema(Vector2Id); - bytes32 declaredSchema = Vector2Table.getSchema(); + Schema registeredSchema = StoreCore.getSchema(Vector2Id); + Schema declaredSchema = Vector2Table.getSchema(); assertEq(keccak256(abi.encode(registeredSchema)), keccak256(abi.encode(declaredSchema))); } From 101ba5a880f0eff2f30d40ee0a3c24375fb1e14c Mon Sep 17 00:00:00 2001 From: alvrs Date: Sun, 29 Jan 2023 16:33:36 +0100 Subject: [PATCH 24/82] refactor(solecs): wip data model - add different events for setting record, field and delete --- packages/solecs/v2/IStore.sol | 4 +- packages/solecs/v2/StoreCore.sol | 60 +++++++++++++----- packages/solecs/v2/StoreSwitch.sol | 12 ++-- packages/solecs/v2/StoreView.sol | 6 +- packages/solecs/v2/World.sol | 4 +- packages/solecs/v2/tables/RouteTable.sol | 4 +- packages/solecs/v2/tables/Vector2Table.sol | 4 +- packages/solecs/v2/test/StoreCore.t.sol | 71 ++++++++++++++++++++-- 8 files changed, 128 insertions(+), 37 deletions(-) diff --git a/packages/solecs/v2/IStore.sol b/packages/solecs/v2/IStore.sol index de18a2e410..d8e532e538 100644 --- a/packages/solecs/v2/IStore.sol +++ b/packages/solecs/v2/IStore.sol @@ -14,7 +14,7 @@ interface IStore { function getSchema(bytes32 table) external view returns (Schema schema); // Set full record (including full dynamic data) - function set( + function setRecord( bytes32 table, bytes32[] memory key, PackedCounter encodedDynamicLength, @@ -37,7 +37,7 @@ interface IStore { ) external; // Get full record (including full array) - function get(bytes32 table, bytes32[] memory key) external view returns (bytes memory data); + function getRecord(bytes32 table, bytes32[] memory key) external view returns (bytes memory data); // Get partial data at schema index function getField( diff --git a/packages/solecs/v2/StoreCore.sol b/packages/solecs/v2/StoreCore.sol index 7536613499..af09d9cde1 100644 --- a/packages/solecs/v2/StoreCore.sol +++ b/packages/solecs/v2/StoreCore.sol @@ -11,14 +11,17 @@ import { PackedCounter } from "./PackedCounter.sol"; import { Buffer, Buffer_ } from "./Buffer.sol"; // TODO -// - Make schema a custom data type we can execute methods on, move schema methods to schema library // - Turn all storage pointer to uint256 for consistency (uint256 is better than bytes32 because it's easier to do arithmetic on) // - Change Storage library functions to make it clearer which argument is offset and which is length // - Streamline naming in Storage and Memory libraries (probably just use load and store instead of read and write?) +// - Make packed counter part of the data packet (so it can be used by indexers, and can be returned by the get function) +// - Add different events for updating entire record and updating individual fields (to make integration more explicit) 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 StoreUpdate(bytes32 table, bytes32[] key, uint8 schemaIndex, bytes data); + event StoreSetRecord(bytes32 table, bytes32[] key, bytes data); + event StoreSetField(bytes32 table, bytes32[] key, uint8 schemaIndex, bytes data); + event StoreDeleteRecord(bytes32 table, bytes32[] key); bytes32 internal constant SLOT = keccak256("mud.store"); bytes32 internal constant SCHEMA_TABLE = keccak256("mud.store.table.schema"); @@ -85,7 +88,7 @@ library StoreCore { /** * Set full data record for the given table and key tuple (static and dynamic data) */ - function set( + function setRecord( bytes32 table, bytes32[] memory key, PackedCounter dynamicLength, @@ -126,7 +129,7 @@ library StoreCore { } // Emit event to notify indexers - emit StoreUpdate(table, key, 0, data); + emit StoreSetRecord(table, key, data); } /** @@ -151,7 +154,7 @@ library StoreCore { Storage.write(location, data); // Emit event to notify indexers - emit StoreUpdate(table, key, 0, data); + emit StoreSetRecord(table, key, data); } function setField( @@ -166,6 +169,9 @@ library StoreCore { } else { _setDynamicField(table, key, schema, schemaIndex, data); } + + // Emit event to notify indexers + emit StoreSetField(table, key, schemaIndex, data); } function _setStaticField( @@ -184,9 +190,6 @@ library StoreCore { bytes32 location = _getStaticDataLocation(table, key); uint256 offset = _getStaticDataOffset(schema, schemaIndex); Storage.write(location, offset, data); - - // Emit event to notify indexers - emit StoreUpdate(table, key, schemaIndex, data); } function _setDynamicField( @@ -204,9 +207,36 @@ library StoreCore { // Store the provided value in storage bytes32 dynamicDataLocation = _getDynamicDataLocation(table, key, dynamicSchemaIndex); Storage.write(dynamicDataLocation, data); + } + + function deleteRecord(bytes32 table, bytes32[] memory key) internal { + // Get schema for this table + Schema schema = getSchema(table); + + // Delete static data + bytes32 staticDataLocation = _getStaticDataLocation(table, key); + Storage.write(staticDataLocation, 0, new bytes(schema.staticDataLength())); + + // If there are no dynamic fields, we're done + if (schema.numDynamicFields() == 0) return; + + // Delete dynamic data length + bytes32 dynamicDataLengthLocation = _getDynamicDataLengthLocation(table, key); + Storage.write(dynamicDataLengthLocation, bytes32(0)); + + // Delete dynamic data + bytes32 dynamicDataLocation; + PackedCounter encodedLengths = _loadEncodedDynamicDataLength(table, key); + for (uint8 i; i < schema.numDynamicFields(); ) { + dynamicDataLocation = _getDynamicDataLocation(table, key, i); + Storage.write(dynamicDataLocation, 0, new bytes(encodedLengths.atIndex(i))); + unchecked { + i++; + } + } // Emit event to notify indexers - emit StoreUpdate(table, key, schemaIndex, data); + emit StoreDeleteRecord(table, key); } /************************************************************************ @@ -218,17 +248,17 @@ library StoreCore { /** * Get full static record for the given table and key tuple (loading schema from storage) */ - function get(bytes32 table, bytes32[] memory key) internal view returns (bytes memory) { + function getRecord(bytes32 table, bytes32[] memory key) internal view returns (bytes memory) { // Get schema for this table Schema schema = getSchema(table); - return get(table, key, schema); + return getRecord(table, key, schema); } /** * Get full data (static and dynamic) for the given table and key tuple, with the given schema */ - function get( + function getRecord( bytes32 table, bytes32[] memory key, Schema schema @@ -457,16 +487,16 @@ library StoreCoreExt { * GET DATA * ************************************************************************/ - function get(bytes32 table, bytes32 _key) external view returns (bytes memory) { + function getRecord(bytes32 table, bytes32 _key) external view returns (bytes memory) { bytes32[] memory key = new bytes32[](1); key[0] = _key; - return StoreCore.get(table, key); + return StoreCore.getRecord(table, key); } function getData(bytes32 table, bytes32[2] memory _key) external view returns (bytes memory) { bytes32[] memory key = new bytes32[](2); key[0] = _key[0]; key[1] = _key[1]; - return StoreCore.get(table, key); + return StoreCore.getRecord(table, key); } } diff --git a/packages/solecs/v2/StoreSwitch.sol b/packages/solecs/v2/StoreSwitch.sol index bf922a387a..d06b418ea7 100644 --- a/packages/solecs/v2/StoreSwitch.sol +++ b/packages/solecs/v2/StoreSwitch.sol @@ -39,16 +39,16 @@ library StoreSwitch { } } - function set( + function setRecord( bytes32 table, bytes32[] memory key, PackedCounter encodedDynamicLength, bytes memory data ) internal { if (isDelegateCall()) { - StoreCore.set(table, key, encodedDynamicLength, data); + StoreCore.setRecord(table, key, encodedDynamicLength, data); } else { - IStore(msg.sender).set(table, key, encodedDynamicLength, data); + IStore(msg.sender).setRecord(table, key, encodedDynamicLength, data); } } @@ -77,11 +77,11 @@ library StoreSwitch { } } - function get(bytes32 table, bytes32[] memory key) internal view returns (bytes memory) { + function getRecord(bytes32 table, bytes32[] memory key) internal view returns (bytes memory) { if (isDelegateCall()) { - return StoreCore.get(table, key); + return StoreCore.getRecord(table, key); } else { - return IStore(msg.sender).get(table, key); + return IStore(msg.sender).getRecord(table, key); } } diff --git a/packages/solecs/v2/StoreView.sol b/packages/solecs/v2/StoreView.sol index 9bb060df1a..5ce2fb689e 100644 --- a/packages/solecs/v2/StoreView.sol +++ b/packages/solecs/v2/StoreView.sol @@ -19,7 +19,7 @@ contract StoreView is IStore { revert Store_BaseContractNotImplemented(); } - function set( + function setRecord( bytes32, bytes32[] memory, PackedCounter, @@ -68,8 +68,8 @@ contract StoreView is IStore { } // Get full record (including full array) - function get(bytes32 table, bytes32[] memory key) public view virtual returns (bytes memory data) { - data = StoreCore.get(table, key); + function getRecord(bytes32 table, bytes32[] memory key) public view virtual returns (bytes memory data) { + data = StoreCore.getRecord(table, key); } // Get partial data at schema index diff --git a/packages/solecs/v2/World.sol b/packages/solecs/v2/World.sol index 8395e05a44..e5ad6e6fcc 100644 --- a/packages/solecs/v2/World.sol +++ b/packages/solecs/v2/World.sol @@ -24,13 +24,13 @@ contract World is StoreView { StoreCore.registerSchema(table, schema); } - function set( + function setRecord( bytes32 table, bytes32[] memory key, PackedCounter encodedDynamicLength, bytes memory data ) public override { - StoreCore.set(table, key, encodedDynamicLength, data); + StoreCore.setRecord(table, key, encodedDynamicLength, data); } function setStaticData( diff --git a/packages/solecs/v2/tables/RouteTable.sol b/packages/solecs/v2/tables/RouteTable.sol index 6aff26442e..d2e9ff3c5c 100644 --- a/packages/solecs/v2/tables/RouteTable.sol +++ b/packages/solecs/v2/tables/RouteTable.sol @@ -76,14 +76,14 @@ library RouteTable { function get(bytes32 key) internal view returns (Route memory data) { bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = key; - bytes memory blob = StoreSwitch.get(id, keyTuple); + bytes memory blob = StoreSwitch.getRecord(id, keyTuple); return decode(blob); } function get(IStore store, bytes32 key) internal view returns (Route memory data) { bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = key; - bytes memory blob = store.get(id, keyTuple); + bytes memory blob = store.getRecord(id, keyTuple); return decode(blob); } diff --git a/packages/solecs/v2/tables/Vector2Table.sol b/packages/solecs/v2/tables/Vector2Table.sol index 8c1cb649fd..aa7c3398b7 100644 --- a/packages/solecs/v2/tables/Vector2Table.sol +++ b/packages/solecs/v2/tables/Vector2Table.sol @@ -68,14 +68,14 @@ library Vector2Table { function get(bytes32 key) internal view returns (Vector2 memory vec2) { bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = key; - bytes memory blob = StoreSwitch.get(id, keyTuple); + bytes memory blob = StoreSwitch.getRecord(id, keyTuple); return decode(blob); } function get(IStore store, bytes32 key) internal view returns (Vector2 memory vec2) { bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = key; - bytes memory blob = store.get(id, keyTuple); + bytes memory blob = store.getRecord(id, keyTuple); return decode(blob); } diff --git a/packages/solecs/v2/test/StoreCore.t.sol b/packages/solecs/v2/test/StoreCore.t.sol index f25706c3f1..ada0dbb429 100644 --- a/packages/solecs/v2/test/StoreCore.t.sol +++ b/packages/solecs/v2/test/StoreCore.t.sol @@ -137,7 +137,7 @@ contract StoreCoreTest is DSTestPlus { // Get data gas = gasleft(); - bytes memory loadedData = StoreCore.get(table, key, schema); + bytes memory loadedData = StoreCore.getRecord(table, key, schema); gas = gas - gasleft(); console.log("gas used (get, warm): %s", gas); @@ -185,7 +185,7 @@ contract StoreCoreTest is DSTestPlus { // Get data gas = gasleft(); - bytes memory loadedData = StoreCore.get(table, key, schema); + bytes memory loadedData = StoreCore.getRecord(table, key, schema); gas = gas - gasleft(); console.log("gas used (get, warm): %s", gas); @@ -237,13 +237,13 @@ contract StoreCoreTest is DSTestPlus { // Set data uint256 gas = gasleft(); - StoreCore.set(table, key, encodedDynamicLength, data); + StoreCore.setRecord(table, key, encodedDynamicLength, data); gas = gas - gasleft(); console.log("gas used (store complex struct / StoreCore): %s", gas); // Get data gas = gasleft(); - bytes memory loadedData = StoreCore.get(table, key); + bytes memory loadedData = StoreCore.getRecord(table, key); gas = gas - gasleft(); // console.log("gas used (read using StoreCore): %s", gas); @@ -403,8 +403,69 @@ contract StoreCoreTest is DSTestPlus { // Verify all fields are correct assertEq( - keccak256(StoreCore.get(table, key)), + keccak256(StoreCore.getRecord(table, key)), keccak256(bytes.concat(firstDataBytes, secondDataBytes, thirdDataBytes, fourthDataBytes)) ); } + + function testDeleteData() public { + bytes32 table = keccak256("some.table"); + + // Register table's schema + Schema schema = Schema_.encode(SchemaType.Uint128, SchemaType.Uint32Array, SchemaType.Uint32Array); + StoreCore.registerSchema(table, schema); + + bytes16 firstDataBytes = bytes16(0x0102030405060708090a0b0c0d0e0f10); + + bytes memory secondDataBytes; + { + uint32[] memory secondData = new uint32[](2); + secondData[0] = 0x11121314; + secondData[1] = 0x15161718; + secondDataBytes = Bytes.from(secondData); + } + + bytes memory thirdDataBytes; + { + uint32[] memory thirdData = new uint32[](3); + thirdData[0] = 0x191a1b1c; + thirdData[1] = 0x1d1e1f20; + thirdData[2] = 0x21222324; + thirdDataBytes = Bytes.from(thirdData); + } + + PackedCounter encodedDynamicLength; + { + uint16[] memory dynamicLengths = new uint16[](2); + dynamicLengths[0] = uint16(secondDataBytes.length); + dynamicLengths[1] = uint16(thirdDataBytes.length); + encodedDynamicLength = PackedCounter_.pack(dynamicLengths); + } + + // Concat data + bytes memory data = bytes.concat(firstDataBytes, secondDataBytes, thirdDataBytes); + + // Create key + bytes32[] memory key = new bytes32[](1); + key[0] = bytes32("some.key"); + + // Set data + StoreCore.setRecord(table, key, encodedDynamicLength, data); + + // Get data + bytes memory loadedData = StoreCore.getRecord(table, key); + + assertEq(loadedData.length, data.length); + assertEq(keccak256(loadedData), keccak256(data)); + + // Delete data + uint256 gas = gasleft(); + StoreCore.deleteRecord(table, key); + gas = gas - gasleft(); + console.log("gas used (delete): %s", gas); + + // Verify data is deleted + loadedData = StoreCore.getRecord(table, key); + assertEq(keccak256(loadedData), keccak256(new bytes(schema.staticDataLength()))); + } } From c3ceb0120fff6c499e5054997736d4cae8c67c29 Mon Sep 17 00:00:00 2001 From: alvrs Date: Sun, 29 Jan 2023 17:05:10 +0100 Subject: [PATCH 25/82] refactor(solecs): wip data model - make encoded dynamic lengths part of the bytes blob --- packages/solecs/v2/Buffer.sol | 7 +++++ packages/solecs/v2/IStore.sol | 2 -- packages/solecs/v2/PackedCounter.sol | 40 +++++++++++++++++++++++++ packages/solecs/v2/StoreCore.sol | 37 ++++++++++++++++------- packages/solecs/v2/StoreSwitch.sol | 6 ++-- packages/solecs/v2/StoreView.sol | 2 -- packages/solecs/v2/World.sol | 4 +-- packages/solecs/v2/test/StoreCore.t.sol | 11 +++---- 8 files changed, 82 insertions(+), 27 deletions(-) diff --git a/packages/solecs/v2/Buffer.sol b/packages/solecs/v2/Buffer.sol index fee0942103..d47c26b698 100644 --- a/packages/solecs/v2/Buffer.sol +++ b/packages/solecs/v2/Buffer.sol @@ -118,6 +118,13 @@ library Buffer_ { appendUnchecked(self, data); } + /** + * @dev Appends bytes32 to the buffer (checking for overflows) + */ + function append(Buffer self, bytes32 data) internal pure { + append(self, data, 32); + } + /** * @dev Appends _dataLength of the given bytes32 to the buffer (checking for overflows) */ diff --git a/packages/solecs/v2/IStore.sol b/packages/solecs/v2/IStore.sol index d8e532e538..7bd7aa8fa0 100644 --- a/packages/solecs/v2/IStore.sol +++ b/packages/solecs/v2/IStore.sol @@ -3,7 +3,6 @@ pragma solidity >=0.8.0; import { SchemaType } from "./Types.sol"; import { Schema } from "./Schema.sol"; -import { PackedCounter } from "./PackedCounter.sol"; interface IStore { // note: the preimage of the tuple of keys used to index is part of the event, so it can be used by indexers @@ -17,7 +16,6 @@ interface IStore { function setRecord( bytes32 table, bytes32[] memory key, - PackedCounter encodedDynamicLength, bytes memory data ) external; diff --git a/packages/solecs/v2/PackedCounter.sol b/packages/solecs/v2/PackedCounter.sol index 26355f3cd7..cd24a054b1 100644 --- a/packages/solecs/v2/PackedCounter.sol +++ b/packages/solecs/v2/PackedCounter.sol @@ -42,6 +42,46 @@ library PackedCounter_ { return PackedCounter.wrap(packedCounter); } + // Overrides for pack function + function pack(uint16 a) internal pure returns (PackedCounter) { + uint16[] memory counters = new uint16[](1); + counters[0] = a; + return pack(counters); + } + + function pack(uint16 a, uint16 b) internal pure returns (PackedCounter) { + uint16[] memory counters = new uint16[](2); + counters[0] = a; + counters[1] = b; + return pack(counters); + } + + function pack( + uint16 a, + uint16 b, + uint16 c + ) internal pure returns (PackedCounter) { + uint16[] memory counters = new uint16[](3); + counters[0] = a; + counters[1] = b; + counters[2] = c; + return pack(counters); + } + + function pack( + uint16 a, + uint16 b, + uint16 c, + uint16 d + ) internal pure returns (PackedCounter) { + uint16[] memory counters = new uint16[](4); + counters[0] = a; + counters[1] = b; + counters[2] = c; + counters[3] = d; + return pack(counters); + } + /************************************************************************ * * INSTANCE FUNCTIONS diff --git a/packages/solecs/v2/StoreCore.sol b/packages/solecs/v2/StoreCore.sol index af09d9cde1..be6a3c05cf 100644 --- a/packages/solecs/v2/StoreCore.sol +++ b/packages/solecs/v2/StoreCore.sol @@ -15,7 +15,6 @@ import { Buffer, Buffer_ } from "./Buffer.sol"; // - Change Storage library functions to make it clearer which argument is offset and which is length // - Streamline naming in Storage and Memory libraries (probably just use load and store instead of read and write?) // - Make packed counter part of the data packet (so it can be used by indexers, and can be returned by the get function) -// - Add different events for updating entire record and updating individual fields (to make integration more explicit) 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 @@ -91,7 +90,6 @@ library StoreCore { function setRecord( bytes32 table, bytes32[] memory key, - PackedCounter dynamicLength, bytes memory data ) internal { // verify the value has the correct length for the table (based on the table's schema) @@ -100,7 +98,14 @@ library StoreCore { // Verify static data length + dynamic data length matches the given data uint256 staticLength = schema.staticDataLength(); - uint256 expectedLength = staticLength + dynamicLength.total(); + uint256 expectedLength = staticLength; + PackedCounter dynamicLength; + if (schema.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 + } + if (expectedLength != data.length) { revert StoreCore_InvalidDataLength(expectedLength, data.length); } @@ -109,7 +114,10 @@ library StoreCore { bytes32 staticDataLocation = _getStaticDataLocation(table, key); uint256 memoryPointer = Memory.dataPointer(data); Storage.write(staticDataLocation, 0, memoryPointer, staticLength); - memoryPointer += staticLength; // move the memory pointer to the start of the dynamic data + memoryPointer += staticLength + 32; // move the memory pointer to the start of the dynamic data (skip the encoded dynamic length) + + // If there is no dynamic data, we're done + if (schema.numDynamicFields() == 0) return; // Store the dynamic data length at the dynamic data length location bytes32 dynamicDataLengthLocation = _getDynamicDataLengthLocation(table, key); @@ -220,10 +228,6 @@ library StoreCore { // If there are no dynamic fields, we're done if (schema.numDynamicFields() == 0) return; - // Delete dynamic data length - bytes32 dynamicDataLengthLocation = _getDynamicDataLengthLocation(table, key); - Storage.write(dynamicDataLengthLocation, bytes32(0)); - // Delete dynamic data bytes32 dynamicDataLocation; PackedCounter encodedLengths = _loadEncodedDynamicDataLength(table, key); @@ -235,6 +239,10 @@ library StoreCore { } } + // Delete dynamic data length + bytes32 dynamicDataLengthLocation = _getDynamicDataLengthLocation(table, key); + Storage.write(dynamicDataLengthLocation, bytes32(0)); + // Emit event to notify indexers emit StoreDeleteRecord(table, key); } @@ -263,22 +271,29 @@ library StoreCore { bytes32[] memory key, Schema schema ) internal view returns (bytes memory) { - // Get static data length - uint256 staticDataLength = schema.staticDataLength(); + // Get the static data length + uint256 outputLength = schema.staticDataLength(); // Load the dynamic data length if there are dynamic fields PackedCounter dynamicDataLength; uint256 numDynamicFields = schema.numDynamicFields(); if (numDynamicFields > 0) { dynamicDataLength = _loadEncodedDynamicDataLength(table, key); + outputLength += 32 + dynamicDataLength.total(); // encoded length + data } // Allocate a buffer for the full data (static and dynamic) - Buffer buffer = Buffer_.allocate(uint128(staticDataLength + dynamicDataLength.total())); + Buffer buffer = Buffer_.allocate(uint128(outputLength)); // Load the static data from storage and append it to the buffer buffer.append(getStaticData(table, key, schema)); + // Early return if there are no dynamic fields + if (dynamicDataLength.total() == 0) return buffer.toBytes(); + + // Append the encoded dynamic length to the buffer after the static data + buffer.append(dynamicDataLength.unwrap()); + // Append dynamic data to the buffer for (uint8 i; i < numDynamicFields; i++) { uint256 dynamicDataLocation = uint256(_getDynamicDataLocation(table, key, i)); diff --git a/packages/solecs/v2/StoreSwitch.sol b/packages/solecs/v2/StoreSwitch.sol index d06b418ea7..ea50a1a84a 100644 --- a/packages/solecs/v2/StoreSwitch.sol +++ b/packages/solecs/v2/StoreSwitch.sol @@ -5,7 +5,6 @@ import { SchemaType } from "./Types.sol"; import { IStore } from "./IStore.sol"; import { StoreCore } from "./StoreCore.sol"; import { Schema } from "./Schema.sol"; -import { PackedCounter } from "./PackedCounter.sol"; /** * Call IStore functions on self or msg.sender, depending on whether the call is a delegatecall or regular call. @@ -42,13 +41,12 @@ library StoreSwitch { function setRecord( bytes32 table, bytes32[] memory key, - PackedCounter encodedDynamicLength, bytes memory data ) internal { if (isDelegateCall()) { - StoreCore.setRecord(table, key, encodedDynamicLength, data); + StoreCore.setRecord(table, key, data); } else { - IStore(msg.sender).setRecord(table, key, encodedDynamicLength, data); + IStore(msg.sender).setRecord(table, key, data); } } diff --git a/packages/solecs/v2/StoreView.sol b/packages/solecs/v2/StoreView.sol index 5ce2fb689e..6fd2a098d3 100644 --- a/packages/solecs/v2/StoreView.sol +++ b/packages/solecs/v2/StoreView.sol @@ -5,7 +5,6 @@ import { SchemaType } from "./Types.sol"; import { IStore } from "./IStore.sol"; import { StoreCore } from "./StoreCore.sol"; import { Schema } from "./Schema.sol"; -import { PackedCounter } from "./PackedCounter.sol"; // Not abstract, so that it can be used as a base contract for testing and wherever write access is not needed contract StoreView is IStore { @@ -22,7 +21,6 @@ contract StoreView is IStore { function setRecord( bytes32, bytes32[] memory, - PackedCounter, bytes memory ) public virtual { revert Store_BaseContractNotImplemented(); diff --git a/packages/solecs/v2/World.sol b/packages/solecs/v2/World.sol index e5ad6e6fcc..52819b1546 100644 --- a/packages/solecs/v2/World.sol +++ b/packages/solecs/v2/World.sol @@ -7,7 +7,6 @@ import { StoreView } from "./StoreView.sol"; import { SchemaType, ExecutionMode } from "./Types.sol"; import { RouteTable, Route, id as RouteId } from "./tables/RouteTable.sol"; import { Bytes } from "./Bytes.sol"; -import { PackedCounter } from "./PackedCounter.sol"; import { Schema } from "./Schema.sol"; /** @@ -27,10 +26,9 @@ contract World is StoreView { function setRecord( bytes32 table, bytes32[] memory key, - PackedCounter encodedDynamicLength, bytes memory data ) public override { - StoreCore.setRecord(table, key, encodedDynamicLength, data); + StoreCore.setRecord(table, key, data); } function setStaticData( diff --git a/packages/solecs/v2/test/StoreCore.t.sol b/packages/solecs/v2/test/StoreCore.t.sol index ada0dbb429..867509d9e4 100644 --- a/packages/solecs/v2/test/StoreCore.t.sol +++ b/packages/solecs/v2/test/StoreCore.t.sol @@ -229,7 +229,7 @@ contract StoreCoreTest is DSTestPlus { } // Concat data - bytes memory data = bytes.concat(firstDataBytes, secondDataBytes, thirdDataBytes); + bytes memory data = bytes.concat(firstDataBytes, encodedDynamicLength.unwrap(), secondDataBytes, thirdDataBytes); // Create key bytes32[] memory key = new bytes32[](1); @@ -237,7 +237,7 @@ contract StoreCoreTest is DSTestPlus { // Set data uint256 gas = gasleft(); - StoreCore.setRecord(table, key, encodedDynamicLength, data); + StoreCore.setRecord(table, key, data); gas = gas - gasleft(); console.log("gas used (store complex struct / StoreCore): %s", gas); @@ -402,9 +402,10 @@ contract StoreCoreTest is DSTestPlus { assertEq(keccak256(loadedData), keccak256(fourthDataBytes)); // Verify all fields are correct + PackedCounter encodedLengths = PackedCounter_.pack(uint16(thirdDataBytes.length), uint16(fourthDataBytes.length)); assertEq( keccak256(StoreCore.getRecord(table, key)), - keccak256(bytes.concat(firstDataBytes, secondDataBytes, thirdDataBytes, fourthDataBytes)) + keccak256(bytes.concat(firstDataBytes, secondDataBytes, encodedLengths.unwrap(), thirdDataBytes, fourthDataBytes)) ); } @@ -443,14 +444,14 @@ contract StoreCoreTest is DSTestPlus { } // Concat data - bytes memory data = bytes.concat(firstDataBytes, secondDataBytes, thirdDataBytes); + bytes memory data = bytes.concat(firstDataBytes, encodedDynamicLength.unwrap(), secondDataBytes, thirdDataBytes); // Create key bytes32[] memory key = new bytes32[](1); key[0] = bytes32("some.key"); // Set data - StoreCore.setRecord(table, key, encodedDynamicLength, data); + StoreCore.setRecord(table, key, data); // Get data bytes memory loadedData = StoreCore.getRecord(table, key); From ed068ccf706af2617793619110269d2f17f66cde Mon Sep 17 00:00:00 2001 From: alvrs Date: Sun, 29 Jan 2023 17:50:49 +0100 Subject: [PATCH 26/82] test(solecs): wip data model - add mixed table with dynamic data schema --- packages/solecs/v2/Bytes.sol | 15 +++ packages/solecs/v2/IStore.sol | 9 +- packages/solecs/v2/StoreCore.sol | 1 - packages/solecs/v2/StoreSwitch.sol | 12 +++ packages/solecs/v2/StoreView.sol | 11 ++- packages/solecs/v2/Types.sol | 3 + packages/solecs/v2/tables/MixedTable.sol | 120 +++++++++++++++++++++++ packages/solecs/v2/test/Bytes.t.sol | 14 +++ packages/solecs/v2/test/MixedTable.t.sol | 57 +++++++++++ 9 files changed, 239 insertions(+), 3 deletions(-) create mode 100644 packages/solecs/v2/tables/MixedTable.sol create mode 100644 packages/solecs/v2/test/MixedTable.t.sol diff --git a/packages/solecs/v2/Bytes.sol b/packages/solecs/v2/Bytes.sol index 5e5a99e2b5..80d1d31060 100644 --- a/packages/solecs/v2/Bytes.sol +++ b/packages/solecs/v2/Bytes.sol @@ -5,6 +5,8 @@ import { console } from "forge-std/console.sol"; import { Utils } from "./Utils.sol"; import { SchemaType } from "./Types.sol"; import { console } from "forge-std/console.sol"; +import { Buffer, Buffer_ } from "./Buffer.sol"; +import { Cast } from "./Cast.sol"; library Bytes { error Bytes_InputTooShort(); @@ -69,6 +71,12 @@ library Bytes { return _from(ptr, 4); } + function from(string memory input) internal pure returns (bytes memory output) { + assembly { + output := input + } + } + function _from(bytes32 _ptr, uint256 _bytesPerElement) internal pure returns (bytes memory output) { assembly { let inputLength := mload(_ptr) @@ -177,6 +185,13 @@ library Bytes { return output; } + /** + * Converts a tightly packed uint32 array into a regular uint32 array. + */ + function toUint32Array(bytes memory input) internal pure returns (uint32[] memory output) { + return Cast.toUint32Array(Buffer_.fromBytes(input).toArray(4)); + } + /************************************************************************ * * UTILS diff --git a/packages/solecs/v2/IStore.sol b/packages/solecs/v2/IStore.sol index 7bd7aa8fa0..7ea00229a2 100644 --- a/packages/solecs/v2/IStore.sol +++ b/packages/solecs/v2/IStore.sol @@ -34,9 +34,16 @@ interface IStore { bytes memory data ) external; - // Get full record (including full array) + // Get full record (including full array, load table schema from storage) function getRecord(bytes32 table, bytes32[] memory key) external view returns (bytes memory data); + // Get full record (including full array) + function getRecord( + bytes32 table, + bytes32[] memory key, + Schema schema + ) external view returns (bytes memory data); + // Get partial data at schema index function getField( bytes32 table, diff --git a/packages/solecs/v2/StoreCore.sol b/packages/solecs/v2/StoreCore.sol index be6a3c05cf..f7aee92308 100644 --- a/packages/solecs/v2/StoreCore.sol +++ b/packages/solecs/v2/StoreCore.sol @@ -14,7 +14,6 @@ import { Buffer, Buffer_ } from "./Buffer.sol"; // - Turn all storage pointer to uint256 for consistency (uint256 is better than bytes32 because it's easier to do arithmetic on) // - Change Storage library functions to make it clearer which argument is offset and which is length // - Streamline naming in Storage and Memory libraries (probably just use load and store instead of read and write?) -// - Make packed counter part of the data packet (so it can be used by indexers, and can be returned by the get function) 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 diff --git a/packages/solecs/v2/StoreSwitch.sol b/packages/solecs/v2/StoreSwitch.sol index ea50a1a84a..21a7f8561d 100644 --- a/packages/solecs/v2/StoreSwitch.sol +++ b/packages/solecs/v2/StoreSwitch.sol @@ -83,6 +83,18 @@ library StoreSwitch { } } + function getRecord( + bytes32 table, + bytes32[] memory key, + Schema schema + ) internal view returns (bytes memory) { + if (isDelegateCall()) { + return StoreCore.getRecord(table, key, schema); + } else { + return IStore(msg.sender).getRecord(table, key, schema); + } + } + function getField( bytes32 table, bytes32[] memory key, diff --git a/packages/solecs/v2/StoreView.sol b/packages/solecs/v2/StoreView.sol index 6fd2a098d3..a0751593f3 100644 --- a/packages/solecs/v2/StoreView.sol +++ b/packages/solecs/v2/StoreView.sol @@ -65,11 +65,20 @@ contract StoreView is IStore { revert Store_BaseContractNotImplemented(); } - // Get full record (including full array) + // Get full record (including full array, load schema from storage) function getRecord(bytes32 table, bytes32[] memory key) public view virtual returns (bytes memory data) { data = StoreCore.getRecord(table, key); } + // Get full record (including full array) + function getRecord( + bytes32 table, + bytes32[] memory key, + Schema schema + ) public view virtual returns (bytes memory data) { + data = StoreCore.getRecord(table, key, schema); + } + // Get partial data at schema index function getField( bytes32 table, diff --git a/packages/solecs/v2/Types.sol b/packages/solecs/v2/Types.sol index a5e59d75cc..1112cfd0c9 100644 --- a/packages/solecs/v2/Types.sol +++ b/packages/solecs/v2/Types.sol @@ -10,6 +10,7 @@ enum SchemaType { Uint128, Uint256, Uint32Array, + String, Bytes4, Address } @@ -41,6 +42,8 @@ function getStaticByteLength(SchemaType schemaType) pure returns (uint256) { function getElementByteLength(SchemaType schemaType) pure returns (uint256) { if (schemaType == SchemaType.Uint32Array) { return 4; + } else if (schemaType == SchemaType.Uint32Array) { + return 1; } else { return getStaticByteLength(schemaType); } diff --git a/packages/solecs/v2/tables/MixedTable.sol b/packages/solecs/v2/tables/MixedTable.sol new file mode 100644 index 0000000000..dfe8bf9adb --- /dev/null +++ b/packages/solecs/v2/tables/MixedTable.sol @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import { console } from "forge-std/console.sol"; +import { IStore } from "../IStore.sol"; +import { StoreSwitch } from "../StoreSwitch.sol"; +import { StoreCore } from "../StoreCore.sol"; +import { SchemaType } from "../Types.sol"; +import { Bytes } from "../Bytes.sol"; +import { Schema, Schema_ } from "../Schema.sol"; +import { PackedCounter, PackedCounter_ } from "../PackedCounter.sol"; + +// -- User defined schema and id -- + +bytes32 constant id = keccak256("mud.store.table.mixed"); + +struct Mixed { + uint32 u32; + uint128 u128; + uint32[] a32; + string s; +} + +// -- Autogenerated schema and library -- +// TODO: autogenerate + +library MixedTable { + /** Get the table's schema */ + function getSchema() internal pure returns (Schema schema) { + schema = Schema_.encode(SchemaType.Uint32, SchemaType.Uint128, SchemaType.Uint32Array, SchemaType.String); + } + + /** Register the table's schema */ + function registerSchema() internal { + StoreSwitch.registerSchema(id, getSchema()); + } + + function registerSchema(IStore store) internal { + store.registerSchema(id, getSchema()); + } + + /** Set the table's data */ + function set( + bytes32 key, + uint32 u32, + uint128 u128, + uint32[] memory a32, + string memory s + ) internal { + PackedCounter encodedLengths = PackedCounter_.pack(uint16(a32.length * 4), uint16(Bytes.from(s).length)); + + bytes memory data = bytes.concat( + bytes4(u32), + bytes16(u128), + encodedLengths.unwrap(), + Bytes.from(a32), + Bytes.from(s) + ); + + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = key; + + StoreSwitch.setRecord(id, keyTuple, data); + } + + function set(bytes32 key, Mixed memory mixed) internal { + set(key, mixed.u32, mixed.u128, mixed.a32, mixed.s); + } + + function setU32(bytes32 key, uint32 u32) internal { + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = key; + StoreSwitch.setField(id, keyTuple, 0, bytes.concat(bytes4(u32))); + } + + function setU128(bytes32 key, uint128 u128) internal { + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = key; + StoreSwitch.setField(id, keyTuple, 1, bytes.concat(bytes16(u128))); + } + + function setA32(bytes32 key, uint32[] memory a32) internal { + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = key; + StoreSwitch.setField(id, keyTuple, 2, Bytes.from(a32)); + } + + function setS(bytes32 key, string memory s) internal { + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = key; + StoreSwitch.setField(id, keyTuple, 3, Bytes.from(s)); + } + + /** Get the table's data */ + function get(bytes32 key) internal view returns (Mixed memory mixed) { + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = key; + bytes memory blob = StoreSwitch.getRecord(id, keyTuple, getSchema()); + return decode(blob); + } + + function get(IStore store, bytes32 key) internal view returns (Mixed memory mixed) { + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = key; + bytes memory blob = store.getRecord(id, keyTuple); + return decode(blob); + } + + function decode(bytes memory blob) internal pure returns (Mixed memory mixed) { + PackedCounter encodedLengths = PackedCounter.wrap((Bytes.slice32(blob, 20))); // 20 = 4 + 16 (static data length) + + return + Mixed({ + u32: uint32(Bytes.slice4(blob, 0)), + u128: uint128(Bytes.slice16(blob, 4)), + a32: Bytes.toUint32Array(Bytes.slice(blob, 52, encodedLengths.atIndex(0))), + s: string(Bytes.slice(blob, 52 + encodedLengths.atIndex(0), encodedLengths.atIndex(1))) + }); + } +} diff --git a/packages/solecs/v2/test/Bytes.t.sol b/packages/solecs/v2/test/Bytes.t.sol index 482fd435c3..d9fcb8f2e5 100644 --- a/packages/solecs/v2/test/Bytes.t.sol +++ b/packages/solecs/v2/test/Bytes.t.sol @@ -110,6 +110,20 @@ contract BytesTest is DSTestPlus { assertEq(uint256(output[1]), 0x0300000000000000000000000000000000000000000000000000000000000004); } + function testFromAndToUint32Array() public { + uint32[] memory input = new uint32[](2); + input[0] = 0x01020304; + input[1] = 0x05060708; + + bytes memory tight = Bytes.from(input); + assertEq(tight.length, 8); + + uint32[] memory output = Bytes.toUint32Array(tight); + assertEq(output.length, 2); + assertEq(output[0], 0x01020304); + assertEq(output[1], 0x05060708); + } + function testToBytes32ArrayUneven() public { bytes memory input = new bytes(65); input[0] = 0x01; diff --git a/packages/solecs/v2/test/MixedTable.t.sol b/packages/solecs/v2/test/MixedTable.t.sol new file mode 100644 index 0000000000..1e44251721 --- /dev/null +++ b/packages/solecs/v2/test/MixedTable.t.sol @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import { console } from "forge-std/console.sol"; +import { DSTestPlus } from "solmate/test/utils/DSTestPlus.sol"; +import { MixedTable, id as MixedTableId, Mixed } from "../tables/MixedTable.sol"; +import { StoreCore } from "../StoreCore.sol"; +import { SchemaType } from "../Types.sol"; +import { StoreView } from "../StoreView.sol"; +import { Schema } from "../Schema.sol"; + +contract MixedTableTest is DSTestPlus, StoreView { + Mixed private testMixed; + + function testRegisterAndGetSchema() public { + uint256 gas = gasleft(); + MixedTable.registerSchema(); + gas = gas - gasleft(); + console.log("gas used: %s", gas); + + Schema registeredSchema = StoreCore.getSchema(MixedTableId); + Schema declaredSchema = MixedTable.getSchema(); + + assertEq(keccak256(abi.encode(registeredSchema)), keccak256(abi.encode(declaredSchema))); + } + + function testSetAndGet() public { + MixedTable.registerSchema(); + bytes32 key = keccak256("somekey"); + + uint32[] memory a32 = new uint32[](2); + a32[0] = 3; + a32[1] = 4; + string memory s = "some string"; + + uint256 gas = gasleft(); + MixedTable.set({ key: key, u32: 1, u128: 2, a32: a32, s: s }); + gas = gas - gasleft(); + console.log("gas used (set, StoreCore): %s", gas); + + gas = gasleft(); + Mixed memory mixed = MixedTable.get(key); + gas = gas - gasleft(); + console.log("gas used (get, warm): %s", gas); + + gas = gasleft(); + testMixed = mixed; + gas = gas - gasleft(); + console.log("gas used (set, native solidity): %s", gas); + + assertEq(mixed.u32, 1); + assertEq(mixed.u128, 2); + assertEq(mixed.a32[0], 3); + assertEq(mixed.a32[1], 4); + assertEq(mixed.s, s); + } +} From b5dfeeb7c1e446873602a978d1324996067f530d Mon Sep 17 00:00:00 2001 From: alvrs Date: Mon, 30 Jan 2023 14:04:01 +0100 Subject: [PATCH 27/82] feat(cli): add gas report command --- packages/cli/package.json | 1 + packages/cli/src/commands/gas-report.ts | 215 ++++++++++++++++++++++++ yarn.lock | 26 +++ 3 files changed, 242 insertions(+) create mode 100644 packages/cli/src/commands/gas-report.ts diff --git a/packages/cli/package.json b/packages/cli/package.json index bcab9c02d6..56fcafd2c2 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -63,6 +63,7 @@ "openurl": "^1.1.1", "path": "^0.12.7", "solmate": "https://github.com/Rari-Capital/solmate.git#9cf1428245074e39090dceacb0c28b1f684f584c", + "table": "^6.8.1", "typechain": "^8.1.1", "uuid": "^8.3.2", "yargs": "^17.5.1" diff --git a/packages/cli/src/commands/gas-report.ts b/packages/cli/src/commands/gas-report.ts new file mode 100644 index 0000000000..6de17786f5 --- /dev/null +++ b/packages/cli/src/commands/gas-report.ts @@ -0,0 +1,215 @@ +import type { Arguments, CommandBuilder } from "yargs"; +import { readFileSync, writeFileSync, rmSync } from "fs"; +import { execa } from "execa"; +import chalk from "chalk"; +import { table, getBorderCharacters } from "table"; + +/** + * Print the gas report to the console, save it to a file and compare it to a previous gas report if provided. + * Requires forge to be installed, and gas test files including `// !gasreport` comments, like this: + * + * ```solidity + * contract GasTest is DSTestPlus { + * function testBuffer() public pure { + * // !gasreport allocate a buffer + * Buffer buffer = Buffer_.allocate(32); + * + * bytes32 value = keccak256("some data"); + * + * // !gasreport append 32 bytes to a buffer + * buffer.append(value); + * } + * } + * ``` + */ + +type Options = { + path: string[]; + save?: string; + compare?: string; +}; + +type GasReportEntry = { + source: string; + name: string; + functionCall: string; + gasUsed: number; + prevGasUsed?: number; +}; + +type GasReport = GasReportEntry[]; + +export const command = "gas-report"; +export const desc = "Create a gas report"; + +export const builder: CommandBuilder = (yargs) => + yargs.options({ + path: { type: "array", default: ["Gas.t.sol"], desc: "File containing the gas tests" }, + save: { type: "string", desc: "Save the gas report to a file" }, + compare: { type: "string", desc: "Compare to an existing gas report" }, + }); + +export const handler = async (args: Arguments): Promise => { + const { path, save } = args; + let { compare } = args; + let gasReport: GasReport = []; + + // Iterate through all files provided in the path + for (const file of path) { + gasReport = gasReport.concat(await runGasReport(file)); + } + + // If this gas report should be compared to an existing one, load the existing one + const compareGasReport: GasReport = []; + if (compare) { + try { + const compareFileContents = readFileSync(compare, "utf8"); + // Create a regex to extract the name, function call and gas used + const compareGasReportRegex = new RegExp(/\((.*)\) \| (.*) \[(.*)\]: (.*)/g); + // Loop through the matches and add the resuls to the compareGasReport + let compareGasReportMatch; + while ((compareGasReportMatch = compareGasReportRegex.exec(compareFileContents)) !== null) { + const source = compareGasReportMatch[1]; + const name = compareGasReportMatch[2]; + const functionCall = compareGasReportMatch[3]; + const gasUsed = compareGasReportMatch[4]; + + compareGasReport.push({ source, name, functionCall, gasUsed: parseInt(gasUsed) }); + } + } catch { + console.log(chalk.red(`Gas report to compare not found: ${compare}`)); + compare = undefined; + } + } + + // Merge the previous gas report with the new one + gasReport = gasReport.map((entry) => { + const prevEntry = compareGasReport.find((e) => e.name === entry.name && e.functionCall === entry.functionCall); + return { ...entry, prevGasUsed: prevEntry?.gasUsed }; + }); + + // Print gas report + printGasReport(gasReport, compare); + + // Save gas report to file if requested + if (save) saveGasReport(gasReport, save); + + process.exit(0); +}; + +async function runGasReport(path: string): Promise { + console.log("Running gas report for", chalk.bold(path)); + const gasReport: GasReport = []; + + // Parse the given test file, and add gas reporting wherever requested by a `// !gasreport` comment + const fileContents = readFileSync(path, "utf8"); + let newFile = fileContents; + + // Use a regex to find first line of each function + const functionRegex = new RegExp(/function (.*){/g); + // Insert a line to declare the _gasreport variable at the start of each function + let functionMatch; + while ((functionMatch = functionRegex.exec(fileContents)) !== null) { + const functionSignature = functionMatch[0]; + newFile = newFile.replace(functionSignature, `${functionSignature}\nuint256 _gasreport;`); + } + + // A gasreport comment has a name (written after the comment) and a function call (written on the next line) + // Create a regex to extract both the name and the function call + const regex = new RegExp(/\/\/ !gasreport (.*)\n(.*)/g); + + // Apply the regex and loop through the matches, + // and create a new file with the gasreport comments replaced by the gas report + let match; + let i = 0; + while ((match = regex.exec(fileContents)) !== null) { + const name = match[1]; + const functionCall = match[2].trim(); + + newFile = newFile.replace( + match[0], + ` +_gasreport = gasleft(); +${functionCall} +_gasreport = _gasreport - gasleft(); +console.log("GAS REPORT: ${name} [${functionCall}]:", _gasreport);` + ); + + i++; + } + + // Remove all occurrences of `pure` with `view` + newFile = newFile.replace(/pure/g, "view"); + + // Write the new file to disk (temporarily) + // Create the temp file by replacing the previous file name with MudGasReport + const tempFileName = path.replace(/\.t\.sol$/, "MudGasReport.t.sol"); + writeFileSync(tempFileName, newFile); + + // Run the generated file using forge + const child = execa("forge", ["test", "--match-path", tempFileName, "-vvv"], { + stdio: ["inherit", "pipe", "inherit"], + }); + + // Extrect the logs from the child process + let logs = ""; + try { + logs = (await child).stdout; + rmSync(tempFileName); + } catch (e: any) { + console.log(e.stdout ?? e); + console.log(chalk.red("\n-----------\nError while running the gas report (see above)")); + rmSync(tempFileName); + process.exit(); + } + + // Extract the gas reports from the logs + + // Create a regex to find all lines starting with `GAS REPORT:` and extract the name, function call and gas used + const gasReportRegex = new RegExp(/GAS REPORT: (.*) \[(.*)\]: (.*)/g); + + // Loop through the matches and print the gas report + let gasReportMatch; + while ((gasReportMatch = gasReportRegex.exec(logs)) !== null) { + const name = gasReportMatch[1]; + const functionCall = gasReportMatch[2].replace(";", ""); + const gasUsed = gasReportMatch[3]; + + gasReport.push({ source: path, name, functionCall, gasUsed: parseInt(gasUsed) }); + } + + return gasReport; +} + +function printGasReport(gasReport: GasReport, compare?: string) { + if (compare) console.log(chalk.bold(`Gas report compared to ${compare}`)); + + const headers = [ + chalk.bold("Source"), + chalk.bold("Name"), + chalk.bold("Function call"), + chalk.bold("Gas used"), + ...(compare ? [chalk.bold("Prev gas used"), chalk.bold("Difference")] : []), + ]; + + const values = gasReport.map((entry) => { + const diff = entry.prevGasUsed ? entry.gasUsed - entry.prevGasUsed : 0; + const diffEntry = diff > 0 ? chalk.red(`+${diff}`) : diff < 0 ? chalk.green(`${diff}`) : diff; + const compareColumns = compare ? [entry.prevGasUsed ?? "n/a", diffEntry] : []; + const gasUsedEntry = diff > 0 ? chalk.red(entry.gasUsed) : diff < 0 ? chalk.green(entry.gasUsed) : entry.gasUsed; + return [entry.source, entry.name, entry.functionCall, gasUsedEntry, ...compareColumns]; + }); + + const rows = [headers, ...values]; + + console.log(table(rows, { border: getBorderCharacters("norc") })); +} + +function saveGasReport(gasReport: GasReport, path: string) { + console.log(chalk.bold(`Saving gas report to ${path}`)); + const serializedGasReport = gasReport + .map((entry) => `(${entry.source}) | ${entry.name} [${entry.functionCall}]: ${entry.gasUsed}`) + .join("\n"); + + writeFileSync(path, serializedGasReport); +} diff --git a/yarn.lock b/yarn.lock index 4406ece53c..cbf2439125 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4270,6 +4270,16 @@ ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.6, ajv@^6.6.1, ajv json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ajv@^8.0.1: + version "8.12.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1" + integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + ajv@^8.11.0: version "8.11.0" resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.11.0.tgz#977e91dd96ca669f54a11e23e378e33b884a565f" @@ -10840,6 +10850,11 @@ lodash.templatesettings@^4.0.0: dependencies: lodash._reinterpolate "^3.0.0" +lodash.truncate@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" + integrity sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw== + lodash@4.17.21, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.3.0, lodash@^4.7.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" @@ -14396,6 +14411,17 @@ table@^5.2.3: slice-ansi "^2.1.0" string-width "^3.0.0" +table@^6.8.1: + version "6.8.1" + resolved "https://registry.yarnpkg.com/table/-/table-6.8.1.tgz#ea2b71359fe03b017a5fbc296204471158080bdf" + integrity sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA== + dependencies: + ajv "^8.0.1" + lodash.truncate "^4.4.2" + slice-ansi "^4.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" + tar-fs@^2.0.0, tar-fs@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784" From 1511cbb4bb56de0791ac1665f4dcc19ebc81ec03 Mon Sep 17 00:00:00 2001 From: alvrs Date: Mon, 30 Jan 2023 16:11:31 +0100 Subject: [PATCH 28/82] test(solecs): wip data model - refactor buffer and bytes tests to use mud gas report --- packages/solecs/gas-report.txt | 21 +++++ packages/solecs/v2/Buffer.sol | 13 +++ packages/solecs/v2/test/Buffer.t.sol | 106 ++++++---------------- packages/solecs/v2/test/Bytes.t.sol | 131 +++++++++++---------------- 4 files changed, 114 insertions(+), 157 deletions(-) create mode 100644 packages/solecs/gas-report.txt diff --git a/packages/solecs/gas-report.txt b/packages/solecs/gas-report.txt new file mode 100644 index 0000000000..d9df7b16e0 --- /dev/null +++ b/packages/solecs/gas-report.txt @@ -0,0 +1,21 @@ +(v2/test/Buffer.t.sol) | allocate a buffer [Buffer buf = Buffer_.allocate(32)]: 73 +(v2/test/Buffer.t.sol) | get buffer length [buf.length()]: 87 +(v2/test/Buffer.t.sol) | get buffer pointer [buf.ptr()]: 33 +(v2/test/Buffer.t.sol) | get buffer capacity [buf.capacity()]: 7 +(v2/test/Buffer.t.sol) | append unchecked bytes memory (8) to buffer [buf.appendUnchecked(data1)]: 478 +(v2/test/Buffer.t.sol) | append bytes memory (8) to buffer [buf.append(data2)]: 773 +(v2/test/Buffer.t.sol) | append unchecked bytes8 of bytes32 to buffer [buf.appendUnchecked(data1, 8)]: 351 +(v2/test/Buffer.t.sol) | append bytes8 of bytes32 to buffer [buf.append(data2, 8)]: 645 +(v2/test/Buffer.t.sol) | concat 3 bytes memory (32) using buffer [Buffer buf = Buffer_.concat(data1, data2, data3)]: 2638 +(v2/test/Buffer.t.sol) | concat 3 bytes memory (32) using bytes.concat [bytes memory concat = bytes.concat(data1, data2, data3)]: 641 +(v2/test/Buffer.t.sol) | create a buffer from 8 bytes [Buffer buf = Buffer_.fromBytes(data)]: 40 +(v2/test/Buffer.t.sol) | read bytes32 from buffer [bytes32 value = buf.read32(4)]: 102 +(v2/test/Buffer.t.sol) | read bytes8 with offset 3 from buffer [bytes8 value2 = buf.read8(3)]: 151 +(v2/test/Buffer.t.sol) | read bytes1 with offset 7 from buffer [bytes1 value3 = buf.read1(7)]: 151 +(v2/test/Buffer.t.sol) | set buffer length unchecked [buf._setLengthUnchecked(8)]: 86 +(v2/test/Buffer.t.sol) | set buffer length [buf._setLength(16)]: 192 +(v2/test/Buffer.t.sol) | slice 4 bytes from buffer with offset 4 [bytes memory slice = buf.slice(4, 4)]: 635 +(v2/test/Buffer.t.sol) | convert array pointer to uint256[] [uint256[] memory arr = Cast.toUint256Array(arrayPtr)]: 10 +(v2/test/Buffer.t.sol) | buffer toArray with element length 4 [uint256 arrayPtr = buf.toArray(4)]: 663 +(v2/test/Buffer.t.sol) | convert array pointer to uint32[] [uint32[] memory arr = Cast.toUint32Array(arrayPtr)]: 10 +(v2/test/Buffer.t.sol) | buffer (32 bytes) to bytes memory [bytes memory bufferData = buf.toBytes()]: 90 \ No newline at end of file diff --git a/packages/solecs/v2/Buffer.sol b/packages/solecs/v2/Buffer.sol index d47c26b698..f1037d99d5 100644 --- a/packages/solecs/v2/Buffer.sol +++ b/packages/solecs/v2/Buffer.sol @@ -53,6 +53,19 @@ library Buffer_ { return Buffer.wrap((_ptr << 128) | uint128(data.length)); } + // TODO: add more overloads + function concat( + bytes memory data1, + bytes memory data2, + bytes memory data3 + ) internal pure returns (Buffer) { + Buffer buffer = allocate(uint128(data1.length + data2.length + data3.length)); + buffer.append(data1); + buffer.append(data2); + buffer.append(data3); + return buffer; + } + /************************************************************************ * * INSTANCE FUNCTIONS diff --git a/packages/solecs/v2/test/Buffer.t.sol b/packages/solecs/v2/test/Buffer.t.sol index 3bf504ed0a..0da3aa7bdc 100644 --- a/packages/solecs/v2/test/Buffer.t.sol +++ b/packages/solecs/v2/test/Buffer.t.sol @@ -8,25 +8,17 @@ import "../Buffer.sol"; contract BufferTest is DSTestPlus { function testAllocateBuffer() public { - uint256 gas = gasleft(); + // !gasreport allocate a buffer Buffer buf = Buffer_.allocate(32); - gas = gas - gasleft(); - console.log("gas used (Buffer_.allocate): %s", gas); - gas = gasleft(); + // !gasreport get buffer length buf.length(); - gas = gas - gasleft(); - console.log("gas used (length): %s", gas); - gas = gasleft(); + // !gasreport get buffer pointer buf.ptr(); - gas = gas - gasleft(); - console.log("gas used (ptr): %s", gas); - gas = gasleft(); + // !gasreport get buffer capacity buf.capacity(); - gas = gas - gasleft(); - console.log("gas used (capacity): %s", gas); assertEq(uint256(buf.length()), 0); assertEq(uint256(buf.capacity()), 32); @@ -34,10 +26,9 @@ contract BufferTest is DSTestPlus { function testFromBytes() public { bytes memory data = bytes.concat(bytes8(0x0102030405060708)); - uint256 gas = gasleft(); + + // !gasreport create a buffer from 8 bytes Buffer buf = Buffer_.fromBytes(data); - gas = gas - gasleft(); - console.log("gas used (fromBytes): %s", gas); assertEq(uint256(buf.length()), 8); assertEq(uint256(buf.capacity()), 8); @@ -54,15 +45,11 @@ contract BufferTest is DSTestPlus { function testSetLength() public { Buffer buf = Buffer_.allocate(32); - uint256 gas = gasleft(); + // !gasreport set buffer length unchecked buf._setLengthUnchecked(8); - gas = gas - gasleft(); - console.log("gas used (setLengthUnchecked): %s", gas); - gas = gasleft(); + // !gasreport set buffer length buf._setLength(16); - gas = gas - gasleft(); - console.log("gas used (setLength): %s", gas); assertEq(uint256(buf.length()), 16); assertEq(uint256(buf.capacity()), 32); @@ -85,15 +72,11 @@ contract BufferTest is DSTestPlus { bytes memory data1 = bytes.concat(bytes8(0x0102030405060708)); bytes memory data2 = bytes.concat(bytes8(0x090a0b0c0d0e0f10)); - uint256 gas = gasleft(); + // !gasreport append unchecked bytes memory (8) to buffer buf.appendUnchecked(data1); - gas = gas - gasleft(); - console.log("gas used (append unchecked): %s", gas); - gas = gasleft(); + // !gasreport append bytes memory (8) to buffer buf.append(data2); - gas = gas - gasleft(); - console.log("gas used (append): %s", gas); assertEq(uint256(buf.length()), 16); assertEq(buf.read8(0), bytes8(0x0102030405060708)); @@ -117,29 +100,13 @@ contract BufferTest is DSTestPlus { bytes memory data2 = bytes.concat(keccak256("data2")); bytes memory data3 = bytes.concat(keccak256("data3")); - uint256 gas = gasleft(); - Buffer buf = Buffer_.allocate(uint128(data1.length + data2.length + data3.length)); - buf.append(data1); - buf.append(data2); - buf.append(data3); - gas = gas - gasleft(); - console.log("gas used (buffer concat): %s", gas); - - gas = gasleft(); - bytes memory dataAllAtOnce = bytes.concat(data1, data2, data3); - gas = gas - gasleft(); - console.log("gas used (concat all at once): %s", gas); - - gas = gasleft(); - bytes memory dataSeparately = new bytes(0); - dataSeparately = bytes.concat(dataSeparately, data1); - dataSeparately = bytes.concat(dataSeparately, data2); - dataSeparately = bytes.concat(dataSeparately, data3); - gas = gas - gasleft(); - console.log("gas used (concat separately): %s", gas); - - assertEq(keccak256(buf.toBytes()), keccak256(dataAllAtOnce)); - assertEq(keccak256(buf.toBytes()), keccak256(dataSeparately)); + // !gasreport concat 3 bytes memory (32) using buffer + Buffer buf = Buffer_.concat(data1, data2, data3); + + // !gasreport concat 3 bytes memory (32) using bytes.concat + bytes memory concat = bytes.concat(data1, data2, data3); + + assertEq(keccak256(buf.toBytes()), keccak256(concat)); } function testAppendFixed() public { @@ -147,15 +114,11 @@ contract BufferTest is DSTestPlus { bytes32 data1 = bytes32(bytes8(0x0102030405060708)); bytes32 data2 = bytes32(bytes8(0x090a0b0c0d0e0f10)); - uint256 gas = gasleft(); + // !gasreport append unchecked bytes8 of bytes32 to buffer buf.appendUnchecked(data1, 8); - gas = gas - gasleft(); - console.log("gas used (append unchecked): %s", gas); - gas = gasleft(); + // !gasreport append bytes8 of bytes32 to buffer buf.append(data2, 8); - gas = gas - gasleft(); - console.log("gas used (append): %s", gas); assertEq(uint256(buf.length()), 16); assertEq(buf.read8(0), bytes8(0x0102030405060708)); @@ -171,10 +134,8 @@ contract BufferTest is DSTestPlus { buf.append(data1); buf.append(data2); - uint256 gas = gasleft(); + // !gasreport buffer (32 bytes) to bytes memory bytes memory bufferData = buf.toBytes(); - gas = gas - gasleft(); - console.log("gas used (toBytes): %s", gas); assertEq(keccak256(bufferData), keccak256(data)); } @@ -184,24 +145,18 @@ contract BufferTest is DSTestPlus { bytes memory data = bytes.concat(bytes8(0x0102030405060708)); buf.append(data); - uint256 gas = gasleft(); + // !gasreport read bytes32 from buffer bytes32 value = buf.read32(4); - gas = gas - gasleft(); - console.log("gas used (read32): %s", gas); assertEq(value, bytes32(bytes4(0x05060708))); - gas = gasleft(); + // !gasreport read bytes8 with offset 3 from buffer bytes8 value2 = buf.read8(3); - gas = gas - gasleft(); - console.log("gas used (read8): %s", gas); assertEq(value2, bytes8(bytes5(0x0405060708))); - gas = gasleft(); + // !gasreport read bytes1 with offset 7 from buffer bytes1 value3 = buf.read1(7); - gas = gas - gasleft(); - console.log("gas used (read1): %s", gas); assertEq(value3, bytes1(0x08)); } @@ -211,10 +166,10 @@ contract BufferTest is DSTestPlus { bytes memory data1 = bytes.concat(bytes8(0x0102030405060708)); buf.append(data1); - uint256 gas = gasleft(); + // !gasreport buffer toArray with element length 4 uint256 arrayPtr = buf.toArray(4); - gas = gas - gasleft(); - console.log("gas used (toArray uint32[]): %s", gas); + + // !gasreport convert array pointer to uint32[] uint32[] memory arr = Cast.toUint32Array(arrayPtr); assertEq(arr.length, 2); @@ -227,10 +182,9 @@ contract BufferTest is DSTestPlus { bytes memory data = bytes.concat(bytes8(0x0102030405060708)); buf.append(data); - uint256 gas = gasleft(); uint256 arrayPtr = buf.toArray(4); - gas = gas - gasleft(); - console.log("gas used (toArray uint256[]): %s", gas); + + // !gasreport convert array pointer to uint256[] uint256[] memory arr = Cast.toUint256Array(arrayPtr); assertEq(arr.length, 2); @@ -256,10 +210,8 @@ contract BufferTest is DSTestPlus { bytes memory data = bytes.concat(bytes8(0x0102030405060708)); buf.append(data); - uint256 gas = gasleft(); + // !gasreport slice 4 bytes from buffer with offset 4 bytes memory slice = buf.slice(4, 4); - gas = gas - gasleft(); - console.log("gas used (slice): %s", gas); assertEq(uint256(slice.length), 4); assertEq(keccak256(slice), keccak256(bytes.concat(bytes4(0x05060708)))); diff --git a/packages/solecs/v2/test/Bytes.t.sol b/packages/solecs/v2/test/Bytes.t.sol index d9fcb8f2e5..a55289ee1f 100644 --- a/packages/solecs/v2/test/Bytes.t.sol +++ b/packages/solecs/v2/test/Bytes.t.sol @@ -14,10 +14,10 @@ contract BytesTest is DSTestPlus { input[1] = new bytes(32); input[1][0] = 0x03; input[1][31] = 0x04; - uint256 gas = gasleft(); + + // !gasreport create bytes from bytes array bytes memory output = Bytes.from(input); - gas = gas - gasleft(); - console.log("gas used: %s", gas); + assertEq(output.length, 64); assertEq(uint256(Bytes.toBytes32(output, 0)), 0x0100000000000000000000000000000000000000000000000000000000000002); assertEq(uint256(Bytes.toBytes32(output, 32)), 0x0300000000000000000000000000000000000000000000000000000000000004); @@ -27,10 +27,10 @@ contract BytesTest is DSTestPlus { uint8[] memory input = new uint8[](2); input[0] = 0x01; input[1] = 0x02; - uint256 gas = gasleft(); + + // !gasreport create bytes from uint8 array bytes memory output = Bytes.from(input); - gas = gas - gasleft(); - console.log("gas used: %s", gas); + assertEq(output.length, 2); assertEq(uint256(uint8(output[0])), 0x01); assertEq(uint256(uint8(output[1])), 0x02); @@ -41,10 +41,10 @@ contract BytesTest is DSTestPlus { input[0] = 0x0102; input[1] = 0x0304; input[2] = 0x0506; - uint256 gas = gasleft(); + + // !gasreport create bytes from uint16 array bytes memory output = Bytes.from(input); - gas = gas - gasleft(); - console.log("gas used: %s", gas); + assertEq(output.length, 6); assertEq(uint256(uint8(output[0])), 0x01); assertEq(uint256(uint8(output[1])), 0x02); @@ -58,10 +58,10 @@ contract BytesTest is DSTestPlus { uint32[] memory input = new uint32[](2); input[0] = 0x01020304; input[1] = 0x05060708; - uint256 gas = gasleft(); + + // !gasreport create bytes from uint32 array bytes memory output = Bytes.from(input); - gas = gas - gasleft(); - console.log("gas used: %s", gas); + assertEq(output.length, 8); assertEq(uint256(uint8(output[0])), 0x01); assertEq(uint256(uint8(output[1])), 0x02); @@ -77,10 +77,10 @@ contract BytesTest is DSTestPlus { bytes memory input = new bytes(32); input[0] = 0x01; input[31] = 0x02; - uint256 gas = gasleft(); + + // !gasreport create bytes32 from bytes memory with offset 0 bytes32 output = Bytes.toBytes32(input, 0); - gas = gas - gasleft(); - console.log("gas used: %s", gas); + assertEq(uint256(output), 0x0100000000000000000000000000000000000000000000000000000000000002); } @@ -88,10 +88,10 @@ contract BytesTest is DSTestPlus { bytes memory input = new bytes(64); input[0 + 16] = 0x01; input[31 + 16] = 0x02; - uint256 gas = gasleft(); + + // !gasreport create bytes32 from bytes memory with offset 16 bytes32 output = Bytes.toBytes32(input, 16); - gas = gas - gasleft(); - console.log("gas used: %s", gas); + assertEq(uint256(output), 0x0100000000000000000000000000000000000000000000000000000000000002); } @@ -101,10 +101,10 @@ contract BytesTest is DSTestPlus { input[31] = 0x02; input[32] = 0x03; input[63] = 0x04; - uint256 gas = gasleft(); + + // !gasreport create bytes32 array from bytes memory bytes32[] memory output = Bytes.toBytes32Array(input); - gas = gas - gasleft(); - console.log("gas used: %s", gas); + assertEq(output.length, 2); assertEq(uint256(output[0]), 0x0100000000000000000000000000000000000000000000000000000000000002); assertEq(uint256(output[1]), 0x0300000000000000000000000000000000000000000000000000000000000004); @@ -118,7 +118,9 @@ contract BytesTest is DSTestPlus { bytes memory tight = Bytes.from(input); assertEq(tight.length, 8); + // !gasreport create uint32 array from bytes memory uint32[] memory output = Bytes.toUint32Array(tight); + assertEq(output.length, 2); assertEq(output[0], 0x01020304); assertEq(output[1], 0x05060708); @@ -131,10 +133,10 @@ contract BytesTest is DSTestPlus { input[32] = 0x03; input[63] = 0x04; input[64] = 0x05; - uint256 gas = gasleft(); + + // !gasreport create bytes32 array from bytes memory with uneven length bytes32[] memory output = Bytes.toBytes32Array(input); - gas = gas - gasleft(); - console.log("gas used: %s", gas); + assertEq(output.length, 3); assertEq(uint256(output[0]), 0x0100000000000000000000000000000000000000000000000000000000000002); assertEq(uint256(output[1]), 0x0300000000000000000000000000000000000000000000000000000000000004); @@ -144,17 +146,13 @@ contract BytesTest is DSTestPlus { function testFromAndToUint32() public { uint32 input = 0x01000002; - uint256 gas = gasleft(); + // !gasreport create bytes from uint32 bytes memory output = Bytes.from(input); - gas = gas - gasleft(); - console.log("gas used (uint32 -> bytes): %s", gas); assertEq(output.length, 4); - gas = gasleft(); + // !gasreport create uint32 from bytes uint32 output2 = Bytes.toUint32(output); - gas = gas - gasleft(); - console.log("gas used (bytes -> uint32): %s", gas); assertEq(output2, input); } @@ -166,17 +164,13 @@ contract BytesTest is DSTestPlus { function testFromAndToAddress() public { address input = address(0x0100000000000000000000000000000000000002); - uint256 gas = gasleft(); + // !gasreport create bytes from address bytes memory output = Bytes.from(input); - gas = gas - gasleft(); - console.log("gas used (address -> bytes): %s", gas); assertEq(output.length, 20); - gas = gasleft(); + // !gasreport create address from bytes address output2 = Bytes.toAddress(output); - gas = gas - gasleft(); - console.log("gas used (bytes -> address): %s", gas); assertEq(output2, input); } @@ -188,17 +182,13 @@ contract BytesTest is DSTestPlus { function testFromAndToUint8() public { uint8 input = 0x02; - uint256 gas = gasleft(); + // !gasreport create bytes from uint8 bytes memory output = Bytes.fromUint8(input); - gas = gas - gasleft(); - console.log("gas used (uint8 -> bytes): %s", gas); assertEq(output.length, 1); - gas = gasleft(); + // !gasreport create uint8 from bytes uint8 output2 = Bytes.toUint8(output); - gas = gas - gasleft(); - console.log("gas used (bytes -> uint8): %s", gas); assertEq(output2, input); } @@ -210,17 +200,13 @@ contract BytesTest is DSTestPlus { function testFromAndToBytes4() public { bytes4 input = bytes4(0x01000002); - uint256 gas = gasleft(); + // !gasreport create bytes from bytes4 bytes memory output = Bytes.from(input); - gas = gas - gasleft(); - console.log("gas used (bytes4 -> bytes): %s", gas); assertEq(output.length, 4); - gas = gasleft(); + // !gasreport create bytes4 from bytes bytes4 output2 = Bytes.toBytes4(output); - gas = gas - gasleft(); - console.log("gas used (bytes -> bytes4): %s", gas); assertEq(output2, input); } @@ -232,38 +218,35 @@ contract BytesTest is DSTestPlus { function testEquals() public { bytes memory a = bytes("a"); bytes memory b = bytes("a"); - uint256 gas = gasleft(); - assertTrue(Bytes.equals(a, b)); - gas = gas - gasleft(); - console.log("gas used: %s", gas); + + // !gasreport compare equal bytes + bool equals = Bytes.equals(a, b); + + assertTrue(equals); } function testEqualsFalse() public { bytes memory a = bytes("a"); bytes memory b = bytes("b"); - uint256 gas = gasleft(); - assertFalse(Bytes.equals(a, b)); - gas = gas - gasleft(); - console.log("gas used: %s", gas); + + // !gasreport compare unequal bytes + bool equals = Bytes.equals(a, b); + + assertFalse(equals); } function testEqualsFalseDiffLength() public { bytes memory a = bytes("a"); bytes memory b = bytes("aa"); - uint256 gas = gasleft(); assertFalse(Bytes.equals(a, b)); - gas = gas - gasleft(); - console.log("gas used: %s", gas); } function testSetLengthInPlace() public { bytes memory a = new bytes(5); assertEq(a.length, 5); - uint256 gas = gasleft(); + // !gasreport set length of bytes in place Bytes.setLengthInPlace(a, 2); - gas = gas - gasleft(); - console.log("gas used: %s", gas); assertEq(a.length, 2); } @@ -276,10 +259,8 @@ contract BytesTest is DSTestPlus { a[3] = 0x04; a[4] = 0x05; - uint256 gas = gasleft(); + // !gasreport slice bytes (with copying) with offset 1 and length 3 bytes memory b = Bytes.slice(a, 1, 3); - gas = gas - gasleft(); - console.log("gas used: %s", gas); assertEq(b.length, 3); assertEq(uint256(uint8(b[0])), 0x02); @@ -296,10 +277,8 @@ contract BytesTest is DSTestPlus { a[3] = 0x04; a[4] = 0x05; - uint256 gas = gasleft(); + // !gasreport slice bytes3 with offset 1 bytes3 b = Bytes.slice3(a, 1); - gas = gas - gasleft(); - console.log("gas used: %s", gas); assertEq(b.length, 3); assertEq(uint256(uint8(b[0])), 0x02); @@ -311,10 +290,8 @@ contract BytesTest is DSTestPlus { bytes32 original = keccak256("some data"); bytes memory input = bytes.concat(bytes10(keccak256("irrelevant data")), original); - uint256 gas = gasleft(); + // !gasreport slice bytes32 with offset 10 bytes32 output = Bytes.slice32(input, 10); - gas = gas - gasleft(); - console.log("gas used: %s", gas); assertEq(output, original); } @@ -322,10 +299,8 @@ contract BytesTest is DSTestPlus { function testSetBytes1() public { bytes32 input = bytes32(0); - uint256 gas = gasleft(); + // !gasreport set bytes1 in bytes32 Bytes.setBytes1(input, 8, 0xff); - gas = gas - gasleft(); - console.log("gas used: %s", gas); assertEq(Bytes.setBytes1(input, 0, 0x01), bytes32(bytes1(0x01))); assertEq(Bytes.setBytes1(input, 31, 0x01), bytes32(uint256(0x01))); @@ -334,10 +309,8 @@ contract BytesTest is DSTestPlus { function testSetBytes2() public { bytes32 input = bytes32(0); - uint256 gas = gasleft(); + // !gasreport set bytes2 in bytes32 Bytes.setBytes2(input, 8, 0xffff); - gas = gas - gasleft(); - console.log("gas used: %s", gas); assertEq(Bytes.setBytes2(input, 0, 0xffff), bytes32(bytes2(0xffff))); assertEq(Bytes.setBytes2(input, 30, 0xffff), bytes32(uint256(0xffff))); @@ -346,10 +319,8 @@ contract BytesTest is DSTestPlus { function testSetBytes4() public { bytes32 input = bytes32(0); - uint256 gas = gasleft(); + // !gasreport set bytes4 in bytes32 Bytes.setBytes4(input, 8, 0xffffffff); - gas = gas - gasleft(); - console.log("gas used: %s", gas); assertEq(Bytes.setBytes4(input, 0, 0xffffffff), bytes32(bytes4(0xffffffff))); assertEq(Bytes.setBytes4(input, 30, 0xffffffff), bytes32(uint256(0xffff))); From a5f4af9b477760f276796fe76e2d0a80e8a081b1 Mon Sep 17 00:00:00 2001 From: alvrs Date: Tue, 31 Jan 2023 12:31:58 +0100 Subject: [PATCH 29/82] test(solecs): wip data model - add test for gas cost of custom encoding vs abi encode --- packages/solecs/v2/Buffer.sol | 14 ++++++ packages/solecs/v2/IStore.sol | 7 +-- packages/solecs/v2/StoreCore.sol | 1 + packages/solecs/v2/test/Gas.t.sol | 75 +++++++++++++++++++++++++++++++ 4 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 packages/solecs/v2/test/Gas.t.sol diff --git a/packages/solecs/v2/Buffer.sol b/packages/solecs/v2/Buffer.sol index f1037d99d5..9179119d5f 100644 --- a/packages/solecs/v2/Buffer.sol +++ b/packages/solecs/v2/Buffer.sol @@ -66,6 +66,20 @@ library Buffer_ { return buffer; } + function concat( + bytes memory data1, + bytes memory data2, + bytes memory data3, + bytes memory data4 + ) internal pure returns (Buffer) { + Buffer buffer = allocate(uint128(data1.length + data2.length + data3.length + data4.length)); + buffer.append(data1); + buffer.append(data2); + buffer.append(data3); + buffer.append(data4); + return buffer; + } + /************************************************************************ * * INSTANCE FUNCTIONS diff --git a/packages/solecs/v2/IStore.sol b/packages/solecs/v2/IStore.sol index 7ea00229a2..9a1b6cdc43 100644 --- a/packages/solecs/v2/IStore.sol +++ b/packages/solecs/v2/IStore.sol @@ -5,8 +5,9 @@ import { SchemaType } from "./Types.sol"; import { Schema } from "./Schema.sol"; interface IStore { - // note: the preimage of the tuple of keys used to index is part of the event, so it can be used by indexers - event StoreUpdate(bytes32 table, bytes32[] key, uint8 schemaIndex, bytes data); + event StoreSetRecord(bytes32 table, bytes32[] key, bytes data); + event StoreSetField(bytes32 table, bytes32[] key, uint8 schemaIndex, bytes data); + event StoreDeleteRecord(bytes32 table, bytes32[] key); function registerSchema(bytes32 table, Schema schema) external; @@ -19,7 +20,7 @@ interface IStore { bytes memory data ) external; - // Set full record (including full array) + // TODO: remove this function, use setRecord instead function setStaticData( bytes32 table, bytes32[] memory key, diff --git a/packages/solecs/v2/StoreCore.sol b/packages/solecs/v2/StoreCore.sol index f7aee92308..9c79b3cb3a 100644 --- a/packages/solecs/v2/StoreCore.sol +++ b/packages/solecs/v2/StoreCore.sol @@ -141,6 +141,7 @@ library StoreCore { /** * Set full static data record for the given table and key tuple (without any dynamic data) + * TODO: remove this function and just use setRecord */ function setStaticData( bytes32 table, diff --git a/packages/solecs/v2/test/Gas.t.sol b/packages/solecs/v2/test/Gas.t.sol new file mode 100644 index 0000000000..fbbefba4bf --- /dev/null +++ b/packages/solecs/v2/test/Gas.t.sol @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import { console } from "forge-std/console.sol"; +import { DSTestPlus } from "solmate/test/utils/DSTestPlus.sol"; +import { Bytes } from "../Bytes.sol"; +import { Buffer_ } from "../Buffer.sol"; + +struct Mixed { + uint32 u32; + uint128 u128; + uint32[] a32; + string s; +} + +contract SomeContract { + function doSomethingWithBytes(bytes memory data) public {} +} + +contract GasTest is DSTestPlus { + SomeContract someContract = new SomeContract(); + + function testCompareAbiEncodeVsCustom() public { + Mixed memory mixed = Mixed({ u32: 1, u128: 2, a32: new uint32[](3), s: "hello" }); + mixed.a32[0] = 1; + mixed.a32[1] = 2; + mixed.a32[2] = 3; + + // !gasreport abi encode + bytes memory abiEncoded = abi.encode(mixed); + + // !gasreport abi decode + Mixed memory abiDecoded = abi.decode(abiEncoded, (Mixed)); + + // !gasreport abi encode packed + bytes memory abiEncodedPacked = abi.encodePacked(mixed.u32, mixed.u128, mixed.a32, mixed.s); + + // !gasreport custom encode + bytes memory customEncoded = customEncode(mixed); + + // !gasreport custom decode + Mixed memory customDecoded = customDecode(customEncoded); + + console.log( + "Length comparison: abi encode %s, abi encode packed %s, custom %s", + abiEncoded.length, + abiEncodedPacked.length, + customEncoded.length + ); + + // !gasreport pass abi encoded bytes to external contract + someContract.doSomethingWithBytes(abiEncoded); + + // !gasreport pass custom encoded bytes to external contract + someContract.doSomethingWithBytes(customEncoded); + + assertEq(keccak256(abi.encode(abiDecoded)), keccak256(abi.encode(customDecoded))); + } +} + +function customEncode(Mixed memory mixed) pure returns (bytes memory) { + return bytes.concat(bytes4(mixed.u32), bytes16(mixed.u128), Bytes.from(mixed.a32), Bytes.from(mixed.s)); +} + +function customDecode(bytes memory input) view returns (Mixed memory) { + console.log(input.length); + + return + Mixed({ + u32: uint32(Bytes.slice4(input, 0)), + u128: uint128(Bytes.slice16(input, 4)), + a32: Bytes.toUint32Array(Bytes.slice(input, 20, 3 * 4)), + s: string(Bytes.slice(input, 20 + 3 * 4, input.length - 20 - 3 * 4)) + }); +} From 8d36c45555b3336469628df4787751e1c1be4c5e Mon Sep 17 00:00:00 2001 From: alvrs Date: Tue, 31 Jan 2023 18:10:24 +0100 Subject: [PATCH 30/82] feat(solecs): wip data model - add CallbackArray schema and tests, refactor typed lib definitions --- packages/solecs/v2/Buffer.sol | 18 ++- packages/solecs/v2/Bytes.sol | 34 ++++- packages/solecs/v2/Cast.sol | 6 + packages/solecs/v2/StoreCore.sol | 30 ++++ packages/solecs/v2/StoreView.sol | 4 + packages/solecs/v2/Types.sol | 5 +- packages/solecs/v2/World.sol | 2 +- packages/solecs/v2/schemas/CallbackArray.sol | 85 +++++++++++ packages/solecs/v2/schemas/Mixed.sol | 143 ++++++++++++++++++ packages/solecs/v2/schemas/Route.sol | 117 ++++++++++++++ packages/solecs/v2/schemas/Vector2.sol | 100 ++++++++++++ packages/solecs/v2/tables/MixedTable.sol | 77 ++-------- .../v2/tables/OnUpdateFieldHookTable.sol | 55 +++++++ .../v2/tables/OnUpdateRecordHookTable.sol | 55 +++++++ packages/solecs/v2/tables/RouteTable.sol | 57 ++----- packages/solecs/v2/tables/Vector2Table.sol | 50 ++---- packages/solecs/v2/test/Bytes.t.sol | 18 +++ packages/solecs/v2/test/MixedTable.t.sol | 2 +- packages/solecs/v2/test/RouteTable.t.sol | 2 +- packages/solecs/v2/test/Vector2Table.t.sol | 2 +- .../v2/test/schemas/CallbackArray.t.sol | 36 +++++ 21 files changed, 741 insertions(+), 157 deletions(-) create mode 100644 packages/solecs/v2/schemas/CallbackArray.sol create mode 100644 packages/solecs/v2/schemas/Mixed.sol create mode 100644 packages/solecs/v2/schemas/Route.sol create mode 100644 packages/solecs/v2/schemas/Vector2.sol create mode 100644 packages/solecs/v2/tables/OnUpdateFieldHookTable.sol create mode 100644 packages/solecs/v2/tables/OnUpdateRecordHookTable.sol create mode 100644 packages/solecs/v2/test/schemas/CallbackArray.t.sol diff --git a/packages/solecs/v2/Buffer.sol b/packages/solecs/v2/Buffer.sol index 9179119d5f..f4f610aa15 100644 --- a/packages/solecs/v2/Buffer.sol +++ b/packages/solecs/v2/Buffer.sol @@ -232,13 +232,25 @@ library Buffer_ { */ /** - * @dev copies the buffer contents to a new memory location and lays it out like a memory array with the given size per element (in bytes) + * @dev copies the buffer contents to a new memory location and lays it out like a right aligned (eg uint) memory array with the given size per element (in bytes) * @return arrayPtr the pointer to the start of the array, needs to be casted to the expected type using assembly */ function toArray(Buffer self, uint256 elementSize) internal pure returns (uint256 arrayPtr) { + return toArray(self, elementSize, false); + } + + /** + * @dev copies the buffer contents to a new memory location and lays it out like a memory array with the given size per element (in bytes) + * @return arrayPtr the pointer to the start of the array, needs to be casted to the expected type using assembly + */ + function toArray( + Buffer self, + uint256 elementSize, + bool leftAligned + ) internal pure returns (uint256 arrayPtr) { uint256 _ptr = ptr(self); uint128 _length = length(self); - uint256 elementShift = 256 - elementSize * 8; + uint256 padLeft = leftAligned ? 0 : 256 - elementSize * 8; assembly { arrayPtr := mload(0x40) // free memory pointer @@ -256,7 +268,7 @@ library Buffer_ { dataCursor := add(dataCursor, elementSize) // increment buffer pointer by one element size arrayCursor := add(arrayCursor, 32) // increment array pointer by one slot } { - mstore(arrayCursor, shr(elementShift, mload(dataCursor))) // copy one element from buffer to array + mstore(arrayCursor, shr(padLeft, mload(dataCursor))) // copy one element from buffer to array } } } diff --git a/packages/solecs/v2/Bytes.sol b/packages/solecs/v2/Bytes.sol index 80d1d31060..50769f98d2 100644 --- a/packages/solecs/v2/Bytes.sol +++ b/packages/solecs/v2/Bytes.sol @@ -47,13 +47,6 @@ library Bytes { } function from(uint16[] memory input) internal pure returns (bytes memory output) { - // This implementation costs 500 gas per entry: - // output = new bytes(0); - // for (uint256 i; i < input.length; i++) { - // output = bytes.concat(output, bytes2(input[i])); - // } - - // This implementation costs around 100 gas per entry: bytes32 ptr; assembly { ptr := input @@ -71,6 +64,15 @@ library Bytes { return _from(ptr, 4); } + function from(bytes24[] memory input) internal pure returns (bytes memory output) { + bytes32 ptr; + assembly { + ptr := input + } + + return _from(ptr, 24, true); + } + function from(string memory input) internal pure returns (bytes memory output) { assembly { output := input @@ -78,6 +80,16 @@ library Bytes { } function _from(bytes32 _ptr, uint256 _bytesPerElement) internal pure returns (bytes memory output) { + return _from(_ptr, _bytesPerElement, false); + } + + function _from( + bytes32 _ptr, + uint256 _bytesPerElement, + bool leftAligned + ) internal pure returns (bytes memory output) { + uint256 shiftBits = leftAligned ? 0 : (32 - _bytesPerElement) * 8; + assembly { let inputLength := mload(_ptr) let outputBytes := mul(inputLength, _bytesPerElement) @@ -90,7 +102,6 @@ library Bytes { mstore(0x40, add(output, add(32, outputBytes))) let outputPtr := add(output, 32) - let shiftBits := mul(sub(32, _bytesPerElement), 8) for { let inputPtr := add(_ptr, 32) // Start at first element // Stop at last element @@ -192,6 +203,13 @@ library Bytes { return Cast.toUint32Array(Buffer_.fromBytes(input).toArray(4)); } + /** + * Converts a tightly packed bytes24 array into a regular bytes24 array. + */ + function toBytes24Array(bytes memory input) internal pure returns (bytes24[] memory output) { + return Cast.toBytes24Array(Buffer_.fromBytes(input).toArray(24, true)); + } + /************************************************************************ * * UTILS diff --git a/packages/solecs/v2/Cast.sol b/packages/solecs/v2/Cast.sol index f57ba1d914..9079885c6d 100644 --- a/packages/solecs/v2/Cast.sol +++ b/packages/solecs/v2/Cast.sol @@ -13,4 +13,10 @@ library Cast { arr := ptr } } + + function toBytes24Array(uint256 ptr) internal pure returns (bytes24[] memory arr) { + assembly { + arr := ptr + } + } } diff --git a/packages/solecs/v2/StoreCore.sol b/packages/solecs/v2/StoreCore.sol index 9c79b3cb3a..95228b378d 100644 --- a/packages/solecs/v2/StoreCore.sol +++ b/packages/solecs/v2/StoreCore.sol @@ -9,6 +9,8 @@ import { console } from "forge-std/console.sol"; import { Schema } from "./Schema.sol"; import { PackedCounter } from "./PackedCounter.sol"; import { Buffer, Buffer_ } from "./Buffer.sol"; +import { OnUpdateRecordHookTable, tableId as OnUpdateRecordHookTableId } from "./tables/OnUpdateRecordHookTable.sol"; +import { OnUpdateFieldHookTable, tableId as OnUpdateFieldHookTableId } from "./tables/OnUpdateFieldHookTable.sol"; // TODO // - Turn all storage pointer to uint256 for consistency (uint256 is better than bytes32 because it's easier to do arithmetic on) @@ -25,8 +27,20 @@ library StoreCore { bytes32 internal constant SCHEMA_TABLE = keccak256("mud.store.table.schema"); error StoreCore_TableAlreadyExists(bytes32 table); + error StoreCore_TableNotFound(bytes32 table); error StoreCore_NotImplemented(); error StoreCore_InvalidDataLength(uint256 expected, uint256 received); + error StoreCore_NoDynamicField(); + + /** + * Initialize internal tables. + * Consumers must call this function in their constructor. + * TODO: should we turn the schema table into a "proper table" and register it here? + */ + function initialize() internal { + registerSchema(OnUpdateRecordHookTableId, OnUpdateRecordHookTable.getSchema()); + registerSchema(OnUpdateFieldHookTableId, OnUpdateFieldHookTable.getSchema()); + } /************************************************************************ * @@ -77,6 +91,21 @@ library StoreCore { return Schema.wrap(Storage.read(location)); } + /************************************************************************ + * + * REGISTER HOOKS + * + ************************************************************************/ + + /* + * Add a hook to be called when a record is set + */ + function registerHook( + bytes32 table, + function(bytes32, bytes32[] memory, bytes memory) onUpdateRecord, + function(bytes32, bytes32[] memory, uint8, bytes memory) onUpdateField + ) internal {} + /************************************************************************ * * SET DATA @@ -259,6 +288,7 @@ library StoreCore { function getRecord(bytes32 table, bytes32[] memory key) internal view returns (bytes memory) { // Get schema for this table Schema schema = getSchema(table); + if (schema.isEmpty()) revert StoreCore_TableNotFound(table); return getRecord(table, key, schema); } diff --git a/packages/solecs/v2/StoreView.sol b/packages/solecs/v2/StoreView.sol index a0751593f3..8234d0bd6d 100644 --- a/packages/solecs/v2/StoreView.sol +++ b/packages/solecs/v2/StoreView.sol @@ -10,6 +10,10 @@ import { Schema } from "./Schema.sol"; contract StoreView is IStore { error Store_BaseContractNotImplemented(); + constructor() { + StoreCore.initialize(); + } + function getSchema(bytes32 table) public view virtual returns (Schema schema) { schema = StoreCore.getSchema(table); } diff --git a/packages/solecs/v2/Types.sol b/packages/solecs/v2/Types.sol index 1112cfd0c9..5778c1756d 100644 --- a/packages/solecs/v2/Types.sol +++ b/packages/solecs/v2/Types.sol @@ -9,9 +9,10 @@ enum SchemaType { Uint32, Uint128, Uint256, + Bytes4, Uint32Array, + Bytes24Array, String, - Bytes4, Address } @@ -44,6 +45,8 @@ function getElementByteLength(SchemaType schemaType) pure returns (uint256) { return 4; } else if (schemaType == SchemaType.Uint32Array) { return 1; + } else if (schemaType == SchemaType.Bytes24Array) { + return 24; } else { return getStaticByteLength(schemaType); } diff --git a/packages/solecs/v2/World.sol b/packages/solecs/v2/World.sol index 52819b1546..032252e9b5 100644 --- a/packages/solecs/v2/World.sol +++ b/packages/solecs/v2/World.sol @@ -5,7 +5,7 @@ import { console } from "forge-std/console.sol"; import { StoreCore } from "./StoreCore.sol"; import { StoreView } from "./StoreView.sol"; import { SchemaType, ExecutionMode } from "./Types.sol"; -import { RouteTable, Route, id as RouteId } from "./tables/RouteTable.sol"; +import { RouteTable, Route, tableId as RouteId } from "./tables/RouteTable.sol"; import { Bytes } from "./Bytes.sol"; import { Schema } from "./Schema.sol"; diff --git a/packages/solecs/v2/schemas/CallbackArray.sol b/packages/solecs/v2/schemas/CallbackArray.sol new file mode 100644 index 0000000000..1441cd9473 --- /dev/null +++ b/packages/solecs/v2/schemas/CallbackArray.sol @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import { console } from "forge-std/console.sol"; +import { IStore } from "../IStore.sol"; +import { StoreSwitch } from "../StoreSwitch.sol"; +import { StoreCore } from "../StoreCore.sol"; +import { SchemaType } from "../Types.sol"; +import { Bytes } from "../Bytes.sol"; +import { Schema, Schema_ } from "../Schema.sol"; +import { PackedCounter, PackedCounter_ } from "../PackedCounter.sol"; + +// -- User defined schema and tableId -- +struct CallbackArray { + bytes24[] callbacks; +} + +// -- Autogenerated library to interact with tables with this schema -- +// TODO: autogenerate + +library CallbackArray_ { + /** Get the table's schema */ + function getSchema() internal pure returns (Schema schema) { + schema = Schema_.encode(SchemaType.Bytes24Array); + } + + /** Register the table's schema */ + function registerSchema(bytes32 tableId) internal { + StoreSwitch.registerSchema(tableId, getSchema()); + } + + function registerSchema(bytes32 tableId, IStore store) internal { + store.registerSchema(tableId, getSchema()); + } + + /** Set the table's data */ + function set( + bytes32 tableId, + bytes32 key, + bytes24[] memory callbacks + ) internal { + bytes memory data = bytes.concat(Bytes.from(callbacks)); + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = key; + StoreSwitch.setField(tableId, keyTuple, 0, data); + } + + /** + * Push an element to the callbacks array + * TODO: this is super inefficient right now, need to add support for pushing to arrays to the store core library to avoid reading/writing the entire array + */ + function push( + bytes32 tableId, + bytes32 key, + bytes24 callback + ) internal { + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = key; + bytes memory callbacks = bytes.concat(StoreSwitch.getField(tableId, keyTuple, 0), callback); + StoreSwitch.setField(tableId, keyTuple, 0, callbacks); + } + + /** Get the table's data */ + function get(bytes32 tableId, bytes32 key) internal view returns (bytes24[] memory callbacks) { + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = key; + bytes memory blob = StoreSwitch.getRecord(tableId, keyTuple); + return decode(blob); + } + + function get( + bytes32 tableId, + IStore store, + bytes32 key + ) internal view returns (bytes24[] memory callbacks) { + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = key; + bytes memory blob = store.getRecord(tableId, keyTuple); + return decode(blob); + } + + function decode(bytes memory blob) internal pure returns (bytes24[] memory callbacks) { + return Bytes.toBytes24Array(Bytes.slice(blob, 32, blob.length - 32)); + } +} diff --git a/packages/solecs/v2/schemas/Mixed.sol b/packages/solecs/v2/schemas/Mixed.sol new file mode 100644 index 0000000000..0c21bc4115 --- /dev/null +++ b/packages/solecs/v2/schemas/Mixed.sol @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import { console } from "forge-std/console.sol"; +import { IStore } from "../IStore.sol"; +import { StoreSwitch } from "../StoreSwitch.sol"; +import { StoreCore } from "../StoreCore.sol"; +import { SchemaType } from "../Types.sol"; +import { Bytes } from "../Bytes.sol"; +import { Schema, Schema_ } from "../Schema.sol"; +import { PackedCounter, PackedCounter_ } from "../PackedCounter.sol"; + +// -- User defined schema -- + +struct Mixed { + uint32 u32; + uint128 u128; + uint32[] a32; + string s; +} + +// -- Autogenerated library -- +// TODO: autogenerate + +library Mixed_ { + /** Get the table's schema */ + function getSchema() internal pure returns (Schema schema) { + schema = Schema_.encode(SchemaType.Uint32, SchemaType.Uint128, SchemaType.Uint32Array, SchemaType.String); + } + + /** Register the table's schema */ + function registerSchema(bytes32 tableId) internal { + StoreSwitch.registerSchema(tableId, getSchema()); + } + + function registerSchema(bytes32 tableId, IStore store) internal { + store.registerSchema(tableId, getSchema()); + } + + /** Set the table's data */ + function set( + bytes32 tableId, + bytes32 key, + uint32 u32, + uint128 u128, + uint32[] memory a32, + string memory s + ) internal { + PackedCounter encodedLengths = PackedCounter_.pack(uint16(a32.length * 4), uint16(Bytes.from(s).length)); + + bytes memory data = bytes.concat( + bytes4(u32), + bytes16(u128), + encodedLengths.unwrap(), + Bytes.from(a32), + Bytes.from(s) + ); + + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = key; + + StoreSwitch.setRecord(tableId, keyTuple, data); + } + + function set( + bytes32 tableId, + bytes32 key, + Mixed memory mixed + ) internal { + set(tableId, key, mixed.u32, mixed.u128, mixed.a32, mixed.s); + } + + function setU32( + bytes32 tableId, + bytes32 key, + uint32 u32 + ) internal { + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = key; + StoreSwitch.setField(tableId, keyTuple, 0, bytes.concat(bytes4(u32))); + } + + function setU128( + bytes32 tableId, + bytes32 key, + uint128 u128 + ) internal { + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = key; + StoreSwitch.setField(tableId, keyTuple, 1, bytes.concat(bytes16(u128))); + } + + function setA32( + bytes32 tableId, + bytes32 key, + uint32[] memory a32 + ) internal { + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = key; + StoreSwitch.setField(tableId, keyTuple, 2, Bytes.from(a32)); + } + + function setS( + bytes32 tableId, + bytes32 key, + string memory s + ) internal { + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = key; + StoreSwitch.setField(tableId, keyTuple, 3, Bytes.from(s)); + } + + /** Get the table's data */ + function get(bytes32 tableId, bytes32 key) internal view returns (Mixed memory mixed) { + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = key; + bytes memory blob = StoreSwitch.getRecord(tableId, keyTuple, getSchema()); + return decode(blob); + } + + function get( + bytes32 tableId, + IStore store, + bytes32 key + ) internal view returns (Mixed memory mixed) { + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = key; + bytes memory blob = store.getRecord(tableId, keyTuple); + return decode(blob); + } + + function decode(bytes memory blob) internal pure returns (Mixed memory mixed) { + PackedCounter encodedLengths = PackedCounter.wrap((Bytes.slice32(blob, 20))); // 20 = 4 + 16 (static data length) + + return + Mixed({ + u32: uint32(Bytes.slice4(blob, 0)), + u128: uint128(Bytes.slice16(blob, 4)), + a32: Bytes.toUint32Array(Bytes.slice(blob, 52, encodedLengths.atIndex(0))), + s: string(Bytes.slice(blob, 52 + encodedLengths.atIndex(0), encodedLengths.atIndex(1))) + }); + } +} diff --git a/packages/solecs/v2/schemas/Route.sol b/packages/solecs/v2/schemas/Route.sol new file mode 100644 index 0000000000..2e96da7339 --- /dev/null +++ b/packages/solecs/v2/schemas/Route.sol @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import { console } from "forge-std/console.sol"; +import { IStore } from "../IStore.sol"; +import { StoreSwitch } from "../StoreSwitch.sol"; +import { StoreCore } from "../StoreCore.sol"; +import { SchemaType } from "../Types.sol"; +import { Bytes } from "../Bytes.sol"; +import { Schema, Schema_ } from "../Schema.sol"; + +// -- User defined schema -- + +struct Route { + address addr; + bytes4 selector; + uint8 executionMode; +} + +// -- Autogenerated library -- +// TODO: autogenerate + +library Route_ { + /** Get the table's schema */ + function getSchema() internal pure returns (Schema schema) { + schema = Schema_.encode(SchemaType.Address, SchemaType.Bytes4, SchemaType.Uint8); + } + + /** Register the table's schema */ + function registerSchema(bytes32 tableId) internal { + StoreSwitch.registerSchema(tableId, getSchema()); + } + + function registerSchema(bytes32 tableId, IStore store) internal { + store.registerSchema(tableId, getSchema()); + } + + /** Set the table's data */ + function set( + bytes32 tableId, + bytes32 key, + address addr, + bytes4 selector, + uint8 executionMode + ) internal { + bytes memory data = bytes.concat(bytes20(addr), bytes4(selector), bytes1(executionMode)); + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = key; + StoreSwitch.setStaticData(tableId, keyTuple, data); + } + + function set( + bytes32 tableId, + bytes32 key, + Route memory data + ) internal { + set(tableId, key, data.addr, data.selector, data.executionMode); + } + + function setAddress( + bytes32 tableId, + bytes32 key, + address addr + ) internal { + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = key; + StoreSwitch.setField(tableId, keyTuple, 0, bytes.concat(bytes20(addr))); + } + + function setSelector( + bytes32 tableId, + bytes32 key, + bytes4 selector + ) internal { + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = key; + StoreSwitch.setField(tableId, keyTuple, 1, bytes.concat(bytes4(selector))); + } + + function setExecutionMode( + bytes32 tableId, + bytes32 key, + uint8 executionMode + ) internal { + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = key; + StoreSwitch.setField(tableId, keyTuple, 2, bytes.concat(bytes1(executionMode))); + } + + /** Get the table's data */ + function get(bytes32 tableId, bytes32 key) internal view returns (Route memory data) { + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = key; + bytes memory blob = StoreSwitch.getRecord(tableId, keyTuple); + return decode(blob); + } + + function get( + bytes32 tableId, + IStore store, + bytes32 key + ) internal view returns (Route memory data) { + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = key; + bytes memory blob = store.getRecord(tableId, keyTuple); + return decode(blob); + } + + function decode(bytes memory blob) internal pure returns (Route memory data) { + return + Route({ + addr: address(Bytes.slice20(blob, 0)), + selector: Bytes.slice4(blob, 20), + executionMode: uint8(Bytes.slice1(blob, 24)) + }); + } +} diff --git a/packages/solecs/v2/schemas/Vector2.sol b/packages/solecs/v2/schemas/Vector2.sol new file mode 100644 index 0000000000..00349fd407 --- /dev/null +++ b/packages/solecs/v2/schemas/Vector2.sol @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import { console } from "forge-std/console.sol"; +import { IStore } from "../IStore.sol"; +import { StoreSwitch } from "../StoreSwitch.sol"; +import { StoreCore } from "../StoreCore.sol"; +import { SchemaType } from "../Types.sol"; +import { Bytes } from "../Bytes.sol"; +import { Schema, Schema_ } from "../Schema.sol"; + +// -- User defined schema -- + +struct Vector2 { + uint32 x; + uint32 y; +} + +// -- Autogenerated library -- +// TODO: autogenerate + +library Vector2_ { + /** Get the table's schema */ + function getSchema() internal pure returns (Schema schema) { + schema = Schema_.encode(SchemaType.Uint32, SchemaType.Uint32); + } + + /** Register the table's schema */ + function registerSchema(bytes32 tableId) internal { + StoreSwitch.registerSchema(tableId, getSchema()); + } + + function registerSchema(bytes32 tableId, IStore store) internal { + store.registerSchema(tableId, getSchema()); + } + + /** Set the table's data */ + function set( + bytes32 tableId, + bytes32 key, + uint32 x, + uint32 y + ) internal { + bytes memory data = bytes.concat(bytes4(x), bytes4(y)); + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = key; + StoreSwitch.setStaticData(tableId, keyTuple, data); + } + + function set( + bytes32 tableId, + bytes32 key, + Vector2 memory vec2 + ) internal { + set(tableId, key, vec2.x, vec2.y); + } + + function setX( + bytes32 tableId, + bytes32 key, + uint32 x + ) internal { + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = key; + StoreSwitch.setField(tableId, keyTuple, 0, bytes.concat(bytes4(x))); + } + + function setY( + bytes32 tableId, + bytes32 key, + uint32 y + ) internal { + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = key; + StoreSwitch.setField(tableId, keyTuple, 1, bytes.concat(bytes4(y))); + } + + /** Get the table's data */ + function get(bytes32 tableId, bytes32 key) internal view returns (Vector2 memory vec2) { + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = key; + bytes memory blob = StoreSwitch.getRecord(tableId, keyTuple); + return decode(blob); + } + + function get( + bytes32 tableId, + IStore store, + bytes32 key + ) internal view returns (Vector2 memory vec2) { + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = key; + bytes memory blob = store.getRecord(tableId, keyTuple); + return decode(blob); + } + + function decode(bytes memory blob) internal pure returns (Vector2 memory vec2) { + return Vector2({ x: uint32(Bytes.slice4(blob, 0)), y: uint32(Bytes.slice4(blob, 4)) }); + } +} diff --git a/packages/solecs/v2/tables/MixedTable.sol b/packages/solecs/v2/tables/MixedTable.sol index dfe8bf9adb..76dc7c91b6 100644 --- a/packages/solecs/v2/tables/MixedTable.sol +++ b/packages/solecs/v2/tables/MixedTable.sol @@ -9,34 +9,28 @@ import { SchemaType } from "../Types.sol"; import { Bytes } from "../Bytes.sol"; import { Schema, Schema_ } from "../Schema.sol"; import { PackedCounter, PackedCounter_ } from "../PackedCounter.sol"; +import { Mixed, Mixed_ } from "../schemas/Mixed.sol"; -// -- User defined schema and id -- +// -- User defined tableId -- -bytes32 constant id = keccak256("mud.store.table.mixed"); +bytes32 constant tableId = keccak256("mud.store.table.mixed"); -struct Mixed { - uint32 u32; - uint128 u128; - uint32[] a32; - string s; -} - -// -- Autogenerated schema and library -- +// -- Autogenerated library -- // TODO: autogenerate library MixedTable { /** Get the table's schema */ - function getSchema() internal pure returns (Schema schema) { - schema = Schema_.encode(SchemaType.Uint32, SchemaType.Uint128, SchemaType.Uint32Array, SchemaType.String); + function getSchema() internal pure returns (Schema) { + return Mixed_.getSchema(); } /** Register the table's schema */ function registerSchema() internal { - StoreSwitch.registerSchema(id, getSchema()); + Mixed_.registerSchema(tableId); } function registerSchema(IStore store) internal { - store.registerSchema(id, getSchema()); + Mixed_.registerSchema(tableId, store); } /** Set the table's data */ @@ -47,74 +41,35 @@ library MixedTable { uint32[] memory a32, string memory s ) internal { - PackedCounter encodedLengths = PackedCounter_.pack(uint16(a32.length * 4), uint16(Bytes.from(s).length)); - - bytes memory data = bytes.concat( - bytes4(u32), - bytes16(u128), - encodedLengths.unwrap(), - Bytes.from(a32), - Bytes.from(s) - ); - - bytes32[] memory keyTuple = new bytes32[](1); - keyTuple[0] = key; - - StoreSwitch.setRecord(id, keyTuple, data); + Mixed_.set(tableId, key, u32, u128, a32, s); } function set(bytes32 key, Mixed memory mixed) internal { - set(key, mixed.u32, mixed.u128, mixed.a32, mixed.s); + Mixed_.set(tableId, key, mixed.u32, mixed.u128, mixed.a32, mixed.s); } function setU32(bytes32 key, uint32 u32) internal { - bytes32[] memory keyTuple = new bytes32[](1); - keyTuple[0] = key; - StoreSwitch.setField(id, keyTuple, 0, bytes.concat(bytes4(u32))); + Mixed_.setU32(tableId, key, u32); } function setU128(bytes32 key, uint128 u128) internal { - bytes32[] memory keyTuple = new bytes32[](1); - keyTuple[0] = key; - StoreSwitch.setField(id, keyTuple, 1, bytes.concat(bytes16(u128))); + Mixed_.setU128(tableId, key, u128); } function setA32(bytes32 key, uint32[] memory a32) internal { - bytes32[] memory keyTuple = new bytes32[](1); - keyTuple[0] = key; - StoreSwitch.setField(id, keyTuple, 2, Bytes.from(a32)); + Mixed_.setA32(tableId, key, a32); } function setS(bytes32 key, string memory s) internal { - bytes32[] memory keyTuple = new bytes32[](1); - keyTuple[0] = key; - StoreSwitch.setField(id, keyTuple, 3, Bytes.from(s)); + Mixed_.setS(tableId, key, s); } /** Get the table's data */ function get(bytes32 key) internal view returns (Mixed memory mixed) { - bytes32[] memory keyTuple = new bytes32[](1); - keyTuple[0] = key; - bytes memory blob = StoreSwitch.getRecord(id, keyTuple, getSchema()); - return decode(blob); + return Mixed_.get(tableId, key); } function get(IStore store, bytes32 key) internal view returns (Mixed memory mixed) { - bytes32[] memory keyTuple = new bytes32[](1); - keyTuple[0] = key; - bytes memory blob = store.getRecord(id, keyTuple); - return decode(blob); - } - - function decode(bytes memory blob) internal pure returns (Mixed memory mixed) { - PackedCounter encodedLengths = PackedCounter.wrap((Bytes.slice32(blob, 20))); // 20 = 4 + 16 (static data length) - - return - Mixed({ - u32: uint32(Bytes.slice4(blob, 0)), - u128: uint128(Bytes.slice16(blob, 4)), - a32: Bytes.toUint32Array(Bytes.slice(blob, 52, encodedLengths.atIndex(0))), - s: string(Bytes.slice(blob, 52 + encodedLengths.atIndex(0), encodedLengths.atIndex(1))) - }); + return Mixed_.get(tableId, store, key); } } diff --git a/packages/solecs/v2/tables/OnUpdateFieldHookTable.sol b/packages/solecs/v2/tables/OnUpdateFieldHookTable.sol new file mode 100644 index 0000000000..ba742777d2 --- /dev/null +++ b/packages/solecs/v2/tables/OnUpdateFieldHookTable.sol @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import { console } from "forge-std/console.sol"; +import { IStore } from "../IStore.sol"; +import { StoreSwitch } from "../StoreSwitch.sol"; +import { StoreCore } from "../StoreCore.sol"; +import { SchemaType } from "../Types.sol"; +import { Bytes } from "../Bytes.sol"; +import { Schema, Schema_ } from "../Schema.sol"; +import { PackedCounter, PackedCounter_ } from "../PackedCounter.sol"; +import { CallbackArray, CallbackArray_ } from "../schemas/CallbackArray.sol"; + +// -- User defined schema and tableId -- +bytes32 constant tableId = keccak256("mud.store.table.onUpdateFieldHook"); + +// -- Autogenerated library to interact with tables with this schema -- +// TODO: autogenerate + +library OnUpdateFieldHookTable { + /** Get the table's schema */ + function getSchema() internal pure returns (Schema schema) { + return CallbackArray_.getSchema(); + } + + /** Register the table's schema */ + function registerSchema() internal { + CallbackArray_.registerSchema(tableId); + } + + function registerSchema(IStore store) internal { + CallbackArray_.registerSchema(tableId, store); + } + + /** Set the table's data */ + function set(bytes32 key, bytes24[] memory callbacks) internal { + CallbackArray_.set(tableId, key, callbacks); + } + + /** + * Push an element to the callbacks array + */ + function push(bytes32 key, bytes24 callback) internal { + CallbackArray_.push(tableId, key, callback); + } + + /** Get the table's data */ + function get(bytes32 key) internal view returns (bytes24[] memory callbacks) { + return CallbackArray_.get(tableId, key); + } + + function get(IStore store, bytes32 key) internal view returns (bytes24[] memory callbacks) { + return CallbackArray_.get(tableId, store, key); + } +} diff --git a/packages/solecs/v2/tables/OnUpdateRecordHookTable.sol b/packages/solecs/v2/tables/OnUpdateRecordHookTable.sol new file mode 100644 index 0000000000..06134c859e --- /dev/null +++ b/packages/solecs/v2/tables/OnUpdateRecordHookTable.sol @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import { console } from "forge-std/console.sol"; +import { IStore } from "../IStore.sol"; +import { StoreSwitch } from "../StoreSwitch.sol"; +import { StoreCore } from "../StoreCore.sol"; +import { SchemaType } from "../Types.sol"; +import { Bytes } from "../Bytes.sol"; +import { Schema, Schema_ } from "../Schema.sol"; +import { PackedCounter, PackedCounter_ } from "../PackedCounter.sol"; +import { CallbackArray, CallbackArray_ } from "../schemas/CallbackArray.sol"; + +// -- User defined schema and tableId -- +bytes32 constant tableId = keccak256("mud.store.table.onUpdateRecordHook"); + +// -- Autogenerated library to interact with tables with this schema -- +// TODO: autogenerate + +library OnUpdateRecordHookTable { + /** Get the table's schema */ + function getSchema() internal pure returns (Schema schema) { + return CallbackArray_.getSchema(); + } + + /** Register the table's schema */ + function registerSchema() internal { + CallbackArray_.registerSchema(tableId); + } + + function registerSchema(IStore store) internal { + CallbackArray_.registerSchema(tableId, store); + } + + /** Set the table's data */ + function set(bytes32 key, bytes24[] memory callbacks) internal { + CallbackArray_.set(tableId, key, callbacks); + } + + /** + * Push an element to the callbacks array + */ + function push(bytes32 key, bytes24 callback) internal { + CallbackArray_.push(tableId, key, callback); + } + + /** Get the table's data */ + function get(bytes32 key) internal view returns (bytes24[] memory callbacks) { + return CallbackArray_.get(tableId, key); + } + + function get(IStore store, bytes32 key) internal view returns (bytes24[] memory callbacks) { + return CallbackArray_.get(tableId, store, key); + } +} diff --git a/packages/solecs/v2/tables/RouteTable.sol b/packages/solecs/v2/tables/RouteTable.sol index d2e9ff3c5c..124b599e4c 100644 --- a/packages/solecs/v2/tables/RouteTable.sol +++ b/packages/solecs/v2/tables/RouteTable.sol @@ -8,33 +8,28 @@ import { StoreCore } from "../StoreCore.sol"; import { SchemaType } from "../Types.sol"; import { Bytes } from "../Bytes.sol"; import { Schema, Schema_ } from "../Schema.sol"; +import { Route, Route_ } from "../schemas/Route.sol"; -// -- User defined schema and id -- +// -- User defined tableId -- -bytes32 constant id = keccak256("mud.store.table.route"); +bytes32 constant tableId = keccak256("mud.store.table.route"); -struct Route { - address addr; - bytes4 selector; - uint8 executionMode; -} - -// -- Autogenerated schema and library -- +// -- Autogenerated library -- // TODO: autogenerate library RouteTable { /** Get the table's schema */ function getSchema() internal pure returns (Schema schema) { - schema = Schema_.encode(SchemaType.Address, SchemaType.Bytes4, SchemaType.Uint8); + return Route_.getSchema(); } /** Register the table's schema */ function registerSchema() internal { - StoreSwitch.registerSchema(id, getSchema()); + Route_.registerSchema(tableId); } function registerSchema(IStore store) internal { - store.registerSchema(id, getSchema()); + Route_.registerSchema(tableId, store); } /** Set the table's data */ @@ -44,55 +39,31 @@ library RouteTable { bytes4 selector, uint8 executionMode ) internal { - bytes memory data = bytes.concat(bytes20(addr), bytes4(selector), bytes1(executionMode)); - bytes32[] memory keyTuple = new bytes32[](1); - keyTuple[0] = key; - StoreSwitch.setStaticData(id, keyTuple, data); + Route_.set(tableId, key, addr, selector, executionMode); } function set(bytes32 key, Route memory data) internal { - set(key, data.addr, data.selector, data.executionMode); + Route_.set(tableId, key, data); } function setAddress(bytes32 key, address addr) internal { - bytes32[] memory keyTuple = new bytes32[](1); - keyTuple[0] = key; - StoreSwitch.setField(id, keyTuple, 0, bytes.concat(bytes20(addr))); + Route_.setAddress(tableId, key, addr); } function setSelector(bytes32 key, bytes4 selector) internal { - bytes32[] memory keyTuple = new bytes32[](1); - keyTuple[0] = key; - StoreSwitch.setField(id, keyTuple, 1, bytes.concat(bytes4(selector))); + Route_.setSelector(tableId, key, selector); } function setExecutionMode(bytes32 key, uint8 executionMode) internal { - bytes32[] memory keyTuple = new bytes32[](1); - keyTuple[0] = key; - StoreSwitch.setField(id, keyTuple, 2, bytes.concat(bytes1(executionMode))); + Route_.setExecutionMode(tableId, key, executionMode); } /** Get the table's data */ function get(bytes32 key) internal view returns (Route memory data) { - bytes32[] memory keyTuple = new bytes32[](1); - keyTuple[0] = key; - bytes memory blob = StoreSwitch.getRecord(id, keyTuple); - return decode(blob); + return Route_.get(tableId, key); } function get(IStore store, bytes32 key) internal view returns (Route memory data) { - bytes32[] memory keyTuple = new bytes32[](1); - keyTuple[0] = key; - bytes memory blob = store.getRecord(id, keyTuple); - return decode(blob); - } - - function decode(bytes memory blob) internal pure returns (Route memory data) { - return - Route({ - addr: address(Bytes.slice20(blob, 0)), - selector: Bytes.slice4(blob, 20), - executionMode: uint8(Bytes.slice1(blob, 24)) - }); + return Route_.get(tableId, store, key); } } diff --git a/packages/solecs/v2/tables/Vector2Table.sol b/packages/solecs/v2/tables/Vector2Table.sol index aa7c3398b7..dd4b081b02 100644 --- a/packages/solecs/v2/tables/Vector2Table.sol +++ b/packages/solecs/v2/tables/Vector2Table.sol @@ -8,32 +8,28 @@ import { StoreCore } from "../StoreCore.sol"; import { SchemaType } from "../Types.sol"; import { Bytes } from "../Bytes.sol"; import { Schema, Schema_ } from "../Schema.sol"; +import { Vector2, Vector2_ } from "../schemas/Vector2.sol"; -// -- User defined schema and id -- +// -- User defined tableId -- -bytes32 constant id = keccak256("mud.store.table.vector3"); +bytes32 constant tableId = keccak256("mud.store.table.vector3"); -struct Vector2 { - uint32 x; - uint32 y; -} - -// -- Autogenerated schema and library -- +// -- Autogenerated library -- // TODO: autogenerate library Vector2Table { /** Get the table's schema */ function getSchema() internal pure returns (Schema schema) { - schema = Schema_.encode(SchemaType.Uint32, SchemaType.Uint32); + return Vector2_.getSchema(); } /** Register the table's schema */ function registerSchema() internal { - StoreSwitch.registerSchema(id, getSchema()); + Vector2_.registerSchema(tableId); } function registerSchema(IStore store) internal { - store.registerSchema(id, getSchema()); + Vector2_.registerSchema(tableId, store); } /** Set the table's data */ @@ -42,51 +38,31 @@ library Vector2Table { uint32 x, uint32 y ) internal { - bytes memory data = bytes.concat(bytes4(x), bytes4(y)); - bytes32[] memory keyTuple = new bytes32[](1); - keyTuple[0] = key; - StoreSwitch.setStaticData(id, keyTuple, data); + Vector2_.set(tableId, key, x, y); } function set(bytes32 key, Vector2 memory vec2) internal { - set(key, vec2.x, vec2.y); + Vector2_.set(tableId, key, vec2); } function setX(bytes32 key, uint32 x) internal { - bytes32[] memory keyTuple = new bytes32[](1); - keyTuple[0] = key; - StoreSwitch.setField(id, keyTuple, 0, bytes.concat(bytes4(x))); + Vector2_.setX(tableId, key, x); } function setY(bytes32 key, uint32 y) internal { - bytes32[] memory keyTuple = new bytes32[](1); - keyTuple[0] = key; - StoreSwitch.setField(id, keyTuple, 1, bytes.concat(bytes4(y))); + Vector2_.setY(tableId, key, y); } /** Get the table's data */ function get(bytes32 key) internal view returns (Vector2 memory vec2) { - bytes32[] memory keyTuple = new bytes32[](1); - keyTuple[0] = key; - bytes memory blob = StoreSwitch.getRecord(id, keyTuple); - return decode(blob); + return Vector2_.get(tableId, key); } function get(IStore store, bytes32 key) internal view returns (Vector2 memory vec2) { - bytes32[] memory keyTuple = new bytes32[](1); - keyTuple[0] = key; - bytes memory blob = store.getRecord(id, keyTuple); - return decode(blob); + return Vector2_.get(tableId, store, key); } function decode(bytes memory blob) internal pure returns (Vector2 memory vec2) { return Vector2({ x: uint32(Bytes.slice4(blob, 0)), y: uint32(Bytes.slice4(blob, 4)) }); - - // Alternative approach, but more expensive: - // return Vector2({ x: Bytes.toUint32(Bytes.slice(blob, 0, 4)), y: Bytes.toUint32(Bytes.slice(blob, 4, 4)) }); - - // Alternative approach, but even more expensive: - // bytes[] memory data = StoreCore.split(blob, getSchema()); - // return Schema({ x: Bytes.toUint32(data[0]), y: Bytes.toUint32(data[1]) }); } } diff --git a/packages/solecs/v2/test/Bytes.t.sol b/packages/solecs/v2/test/Bytes.t.sol index a55289ee1f..6bf641666d 100644 --- a/packages/solecs/v2/test/Bytes.t.sol +++ b/packages/solecs/v2/test/Bytes.t.sol @@ -126,6 +126,24 @@ contract BytesTest is DSTestPlus { assertEq(output[1], 0x05060708); } + function testToAndFromBytes24Array() public { + bytes24[] memory input = new bytes24[](2); + input[0] = bytes24(0x0102030405060708090a0b0c0d0e0f101112131415161718); + input[1] = bytes24(0x19202122232425262728292a2b2c2d2e2f30313233343536); + + // !gasreport tightly pack bytes24 array into bytes array + bytes memory tight = Bytes.from(input); + + assertEq(tight.length, 48); + + // !gasreport create uint32 array from bytes memory + bytes24[] memory output = Bytes.toBytes24Array(tight); + + assertEq(output.length, 2); + assertEq(output[0], input[0]); + assertEq(output[1], input[1]); + } + function testToBytes32ArrayUneven() public { bytes memory input = new bytes(65); input[0] = 0x01; diff --git a/packages/solecs/v2/test/MixedTable.t.sol b/packages/solecs/v2/test/MixedTable.t.sol index 1e44251721..033274a615 100644 --- a/packages/solecs/v2/test/MixedTable.t.sol +++ b/packages/solecs/v2/test/MixedTable.t.sol @@ -3,7 +3,7 @@ pragma solidity >=0.8.0; import { console } from "forge-std/console.sol"; import { DSTestPlus } from "solmate/test/utils/DSTestPlus.sol"; -import { MixedTable, id as MixedTableId, Mixed } from "../tables/MixedTable.sol"; +import { MixedTable, tableId as MixedTableId, Mixed } from "../tables/MixedTable.sol"; import { StoreCore } from "../StoreCore.sol"; import { SchemaType } from "../Types.sol"; import { StoreView } from "../StoreView.sol"; diff --git a/packages/solecs/v2/test/RouteTable.t.sol b/packages/solecs/v2/test/RouteTable.t.sol index 24d2ae208c..8ef09b9491 100644 --- a/packages/solecs/v2/test/RouteTable.t.sol +++ b/packages/solecs/v2/test/RouteTable.t.sol @@ -3,7 +3,7 @@ pragma solidity >=0.8.0; import { console } from "forge-std/console.sol"; import { DSTestPlus } from "solmate/test/utils/DSTestPlus.sol"; -import { RouteTable, id as RouteTableId, Route } from "../tables/RouteTable.sol"; +import { RouteTable, tableId as RouteTableId, Route } from "../tables/RouteTable.sol"; import { StoreCore } from "../StoreCore.sol"; import { SchemaType } from "../Types.sol"; import { StoreView } from "../StoreView.sol"; diff --git a/packages/solecs/v2/test/Vector2Table.t.sol b/packages/solecs/v2/test/Vector2Table.t.sol index ed045b96ab..5cc499939b 100644 --- a/packages/solecs/v2/test/Vector2Table.t.sol +++ b/packages/solecs/v2/test/Vector2Table.t.sol @@ -3,7 +3,7 @@ pragma solidity >=0.8.0; import { console } from "forge-std/console.sol"; import { DSTestPlus } from "solmate/test/utils/DSTestPlus.sol"; -import { Vector2Table, id as Vector2Id, Vector2 } from "../tables/Vector2Table.sol"; +import { Vector2Table, tableId as Vector2Id, Vector2 } from "../tables/Vector2Table.sol"; import { StoreCore } from "../StoreCore.sol"; import { SchemaType } from "../Types.sol"; import { StoreView } from "../StoreView.sol"; diff --git a/packages/solecs/v2/test/schemas/CallbackArray.t.sol b/packages/solecs/v2/test/schemas/CallbackArray.t.sol new file mode 100644 index 0000000000..7e739eaa79 --- /dev/null +++ b/packages/solecs/v2/test/schemas/CallbackArray.t.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import { console } from "forge-std/console.sol"; +import { DSTestPlus } from "solmate/test/utils/DSTestPlus.sol"; +import { StoreView } from "../../StoreView.sol"; +import { CallbackArray, CallbackArray_ } from "../../schemas/CallbackArray.sol"; + +bytes32 constant tableId = keccak256("mud.store.table.callbackArray"); + +contract CallbackArrayTest is DSTestPlus, StoreView { + function testSetAndGet() public { + CallbackArray_.registerSchema(tableId); + bytes32 key = keccak256("somekey"); + + bytes24[] memory callbacks = new bytes24[](1); + callbacks[0] = bytes24(abi.encode(this.testSetAndGet)); + + // !gasreport set record in CallbackArrayTable + CallbackArray_.set(tableId, key, callbacks); + + // !gasreport get record from CallbackArrayTable (warm) + bytes24[] memory returnedCallbacks = CallbackArray_.get(tableId, key); + + assertEq(returnedCallbacks.length, callbacks.length); + assertEq(returnedCallbacks[0], callbacks[0]); + + // !gasreport push record to CallbackArrayTable + CallbackArray_.push(tableId, key, callbacks[0]); + + returnedCallbacks = CallbackArray_.get(tableId, key); + + assertEq(returnedCallbacks.length, 2); + assertEq(returnedCallbacks[1], callbacks[0]); + } +} From ebabe020a3f2f95756c279f4e8ad158b38b9f97e Mon Sep 17 00:00:00 2001 From: alvrs Date: Tue, 31 Jan 2023 21:13:39 +0100 Subject: [PATCH 31/82] feat(solecs): wip data model - add indexer functionality --- packages/solecs/v2/Bytes.sol | 24 +++ packages/solecs/v2/Cast.sol | 6 + packages/solecs/v2/IStore.sol | 15 ++ packages/solecs/v2/StoreCore.sol | 34 +++- packages/solecs/v2/StoreSwitch.sol | 11 ++ packages/solecs/v2/Types.sol | 3 +- packages/solecs/v2/schemas/AddressArray.sol | 86 ++++++++++ ...eldHookTable.sol => OnUpdateHookTable.sol} | 30 ++-- .../v2/tables/OnUpdateRecordHookTable.sol | 55 ------ packages/solecs/v2/test/StoreCore.t.sol | 162 +++++++++++++++++- .../solecs/v2/test/schemas/AddressArray.t.sol | 36 ++++ 11 files changed, 381 insertions(+), 81 deletions(-) create mode 100644 packages/solecs/v2/schemas/AddressArray.sol rename packages/solecs/v2/tables/{OnUpdateFieldHookTable.sol => OnUpdateHookTable.sol} (55%) delete mode 100644 packages/solecs/v2/tables/OnUpdateRecordHookTable.sol create mode 100644 packages/solecs/v2/test/schemas/AddressArray.t.sol diff --git a/packages/solecs/v2/Bytes.sol b/packages/solecs/v2/Bytes.sol index 50769f98d2..859c582b0b 100644 --- a/packages/solecs/v2/Bytes.sol +++ b/packages/solecs/v2/Bytes.sol @@ -79,6 +79,15 @@ library Bytes { } } + function from(address[] memory input) internal pure returns (bytes memory output) { + bytes32 ptr; + assembly { + ptr := input + } + + return _from(ptr, 20); + } + function _from(bytes32 _ptr, uint256 _bytesPerElement) internal pure returns (bytes memory output) { return _from(_ptr, _bytesPerElement, false); } @@ -203,6 +212,13 @@ library Bytes { return Cast.toUint32Array(Buffer_.fromBytes(input).toArray(4)); } + /** + * Converts a tightly packed address array into a regular address array. + */ + function toAddressArray(bytes memory input) internal pure returns (address[] memory output) { + return Cast.toAddressArray(Buffer_.fromBytes(input).toArray(20)); + } + /** * Converts a tightly packed bytes24 array into a regular bytes24 array. */ @@ -411,6 +427,14 @@ library Bytes { return output; } + function slice4(bytes32 data, uint256 start) internal pure returns (bytes4) { + bytes2 output; + assembly { + output := shl(mul(8, start), data) + } + return output; + } + /** Slice bytes to bytes5 without copying data */ function slice5(bytes memory data, uint256 start) internal pure returns (bytes5) { bytes5 output; diff --git a/packages/solecs/v2/Cast.sol b/packages/solecs/v2/Cast.sol index 9079885c6d..3e8d1aa1c0 100644 --- a/packages/solecs/v2/Cast.sol +++ b/packages/solecs/v2/Cast.sol @@ -19,4 +19,10 @@ library Cast { arr := ptr } } + + function toAddressArray(uint256 ptr) internal pure returns (address[] memory arr) { + assembly { + arr := ptr + } + } } diff --git a/packages/solecs/v2/IStore.sol b/packages/solecs/v2/IStore.sol index 9a1b6cdc43..d815c69bc2 100644 --- a/packages/solecs/v2/IStore.sol +++ b/packages/solecs/v2/IStore.sol @@ -56,3 +56,18 @@ interface IStore { // TODO: benchmark this vs. using a known storage slot to determine whether a contract is a Store function isStore() external view; } + +interface IOnUpdateHook { + function onUpdateRecord( + bytes32 table, + bytes32[] memory key, + bytes memory data + ) external; + + function onUpdateField( + bytes32 table, + bytes32[] memory key, + uint8 schemaIndex, + bytes memory data + ) external; +} diff --git a/packages/solecs/v2/StoreCore.sol b/packages/solecs/v2/StoreCore.sol index 95228b378d..6e6ee9411a 100644 --- a/packages/solecs/v2/StoreCore.sol +++ b/packages/solecs/v2/StoreCore.sol @@ -9,8 +9,8 @@ import { console } from "forge-std/console.sol"; import { Schema } from "./Schema.sol"; import { PackedCounter } from "./PackedCounter.sol"; import { Buffer, Buffer_ } from "./Buffer.sol"; -import { OnUpdateRecordHookTable, tableId as OnUpdateRecordHookTableId } from "./tables/OnUpdateRecordHookTable.sol"; -import { OnUpdateFieldHookTable, tableId as OnUpdateFieldHookTableId } from "./tables/OnUpdateFieldHookTable.sol"; +import { OnUpdateHookTable, tableId as OnUpdateHookTableId } from "./tables/OnUpdateHookTable.sol"; +import { IOnUpdateHook } from "./IStore.sol"; // TODO // - Turn all storage pointer to uint256 for consistency (uint256 is better than bytes32 because it's easier to do arithmetic on) @@ -38,8 +38,7 @@ library StoreCore { * TODO: should we turn the schema table into a "proper table" and register it here? */ function initialize() internal { - registerSchema(OnUpdateRecordHookTableId, OnUpdateRecordHookTable.getSchema()); - registerSchema(OnUpdateFieldHookTableId, OnUpdateFieldHookTable.getSchema()); + registerSchema(OnUpdateHookTableId, OnUpdateHookTable.getSchema()); } /************************************************************************ @@ -100,11 +99,9 @@ library StoreCore { /* * Add a hook to be called when a record is set */ - function registerHook( - bytes32 table, - function(bytes32, bytes32[] memory, bytes memory) onUpdateRecord, - function(bytes32, bytes32[] memory, uint8, bytes memory) onUpdateField - ) internal {} + function registerOnUpdateHook(bytes32 table, IOnUpdateHook onUpdateHook) internal { + OnUpdateHookTable.push(table, address(onUpdateHook)); + } /************************************************************************ * @@ -138,6 +135,13 @@ library StoreCore { revert StoreCore_InvalidDataLength(expectedLength, data.length); } + // Call update hooks (before actually modifying the state, so observers have access to the previous state if needed) + address[] memory onUpdateHooks = OnUpdateHookTable.get(table); + for (uint256 i = 0; i < onUpdateHooks.length; i++) { + IOnUpdateHook hook = IOnUpdateHook(onUpdateHooks[i]); + hook.onUpdateRecord(table, key, data); + } + // Store the static data at the static data location bytes32 staticDataLocation = _getStaticDataLocation(table, key); uint256 memoryPointer = Memory.dataPointer(data); @@ -201,6 +205,14 @@ library StoreCore { bytes memory data ) internal { Schema schema = getSchema(table); + + // Call update hooks (before actually modifying the state, so observers have access to the previous state if needed) + address[] memory onUpdateHooks = OnUpdateHookTable.get(table); + for (uint256 i = 0; i < onUpdateHooks.length; i++) { + IOnUpdateHook hook = IOnUpdateHook(onUpdateHooks[i]); + hook.onUpdateField(table, key, schemaIndex, data); + } + if (schemaIndex < schema.numStaticFields()) { _setStaticField(table, key, schema, schemaIndex, data); } else { @@ -350,6 +362,8 @@ library StoreCore { bytes32[] memory key, Schema schema ) internal view returns (bytes memory) { + if (schema.staticDataLength() == 0) return new bytes(0); + // Load the data from storage bytes32 location = _getStaticDataLocation(table, key); return Storage.read(location, schema.staticDataLength()); @@ -399,6 +413,7 @@ library StoreCore { uint256 offset = _getStaticDataOffset(schema, schemaIndex); // Load the data from storage + return Storage.read(location, offset, dataLength); } @@ -415,6 +430,7 @@ library StoreCore { uint8 dynamicSchemaIndex = schemaIndex - schema.numStaticFields(); bytes32 location = _getDynamicDataLocation(table, key, dynamicSchemaIndex); uint256 dataLength = _loadEncodedDynamicDataLength(table, key).atIndex(dynamicSchemaIndex); + return Storage.read(location, dataLength); } diff --git a/packages/solecs/v2/StoreSwitch.sol b/packages/solecs/v2/StoreSwitch.sol index 21a7f8561d..d9a239a33b 100644 --- a/packages/solecs/v2/StoreSwitch.sol +++ b/packages/solecs/v2/StoreSwitch.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; +import { console } from "forge-std/console.sol"; import { SchemaType } from "./Types.sol"; import { IStore } from "./IStore.sol"; import { StoreCore } from "./StoreCore.sol"; @@ -10,11 +11,21 @@ import { Schema } from "./Schema.sol"; * Call IStore functions on self or msg.sender, depending on whether the call is a delegatecall or regular call. */ library StoreSwitch { + error StoreSwitch_InvalidInsideConstructor(); + /** * Detect whether the current call is a delegatecall or regular call. * (The isStore method doesn't return a value to save gas, but it if exists, the call will succeed.) */ function isDelegateCall() internal view returns (bool success) { + // Detect calls from within a constructor and revert to avoid unexpected behavior + uint256 codeSize; + assembly { + codeSize := extcodesize(address()) + } + if (codeSize == 0) revert StoreSwitch_InvalidInsideConstructor(); + + // Check whether this contract implements the IStore interface try IStore(address(this)).isStore() { success = true; } catch { diff --git a/packages/solecs/v2/Types.sol b/packages/solecs/v2/Types.sol index 5778c1756d..f86029a8ba 100644 --- a/packages/solecs/v2/Types.sol +++ b/packages/solecs/v2/Types.sol @@ -13,7 +13,8 @@ enum SchemaType { Uint32Array, Bytes24Array, String, - Address + Address, + AddressArray } /** diff --git a/packages/solecs/v2/schemas/AddressArray.sol b/packages/solecs/v2/schemas/AddressArray.sol new file mode 100644 index 0000000000..5a73191e72 --- /dev/null +++ b/packages/solecs/v2/schemas/AddressArray.sol @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import { console } from "forge-std/console.sol"; +import { IStore } from "../IStore.sol"; +import { StoreSwitch } from "../StoreSwitch.sol"; +import { StoreCore } from "../StoreCore.sol"; +import { SchemaType } from "../Types.sol"; +import { Bytes } from "../Bytes.sol"; +import { Schema, Schema_ } from "../Schema.sol"; +import { PackedCounter, PackedCounter_ } from "../PackedCounter.sol"; + +// -- User defined schema and tableId -- +struct AddressArray { + address[] addresses; +} + +// -- Autogenerated library to interact with tables with this schema -- +// TODO: autogenerate + +library AddressArray_ { + /** Get the table's schema */ + function getSchema() internal pure returns (Schema schema) { + schema = Schema_.encode(SchemaType.AddressArray); + } + + /** Register the table's schema */ + function registerSchema(bytes32 tableId) internal { + StoreSwitch.registerSchema(tableId, getSchema()); + } + + function registerSchema(bytes32 tableId, IStore store) internal { + store.registerSchema(tableId, getSchema()); + } + + /** Set the table's data */ + function set( + bytes32 tableId, + bytes32 key, + address[] memory addresses + ) internal { + bytes memory data = bytes.concat(Bytes.from(addresses)); + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = key; + StoreSwitch.setField(tableId, keyTuple, 0, data); + } + + /** + * Push an element to the addresses array + * TODO: this is super inefficient right now, need to add support for pushing to arrays to the store core library to avoid reading/writing the entire array + */ + function push( + bytes32 tableId, + bytes32 key, + address addr + ) internal { + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = key; + bytes memory addresses = bytes.concat(StoreSwitch.getField(tableId, keyTuple, 0), bytes20(addr)); + StoreSwitch.setField(tableId, keyTuple, 0, addresses); + } + + /** Get the table's data */ + function get(bytes32 tableId, bytes32 key) internal view returns (address[] memory addresses) { + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = key; + bytes memory blob = StoreSwitch.getRecord(tableId, keyTuple); + return decode(blob); + } + + function get( + bytes32 tableId, + IStore store, + bytes32 key + ) internal view returns (address[] memory addresses) { + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = key; + bytes memory blob = store.getRecord(tableId, keyTuple); + return decode(blob); + } + + function decode(bytes memory blob) internal pure returns (address[] memory addresses) { + if (blob.length == 0) return new address[](0); + return Bytes.toAddressArray(Bytes.slice(blob, 32, blob.length - 32)); + } +} diff --git a/packages/solecs/v2/tables/OnUpdateFieldHookTable.sol b/packages/solecs/v2/tables/OnUpdateHookTable.sol similarity index 55% rename from packages/solecs/v2/tables/OnUpdateFieldHookTable.sol rename to packages/solecs/v2/tables/OnUpdateHookTable.sol index ba742777d2..768f5e3e55 100644 --- a/packages/solecs/v2/tables/OnUpdateFieldHookTable.sol +++ b/packages/solecs/v2/tables/OnUpdateHookTable.sol @@ -9,47 +9,47 @@ import { SchemaType } from "../Types.sol"; import { Bytes } from "../Bytes.sol"; import { Schema, Schema_ } from "../Schema.sol"; import { PackedCounter, PackedCounter_ } from "../PackedCounter.sol"; -import { CallbackArray, CallbackArray_ } from "../schemas/CallbackArray.sol"; +import { AddressArray, AddressArray_ } from "../schemas/AddressArray.sol"; // -- User defined schema and tableId -- -bytes32 constant tableId = keccak256("mud.store.table.onUpdateFieldHook"); +bytes32 constant tableId = keccak256("mud.store.table.onUpdateHookTable"); // -- Autogenerated library to interact with tables with this schema -- // TODO: autogenerate -library OnUpdateFieldHookTable { +library OnUpdateHookTable { /** Get the table's schema */ function getSchema() internal pure returns (Schema schema) { - return CallbackArray_.getSchema(); + return AddressArray_.getSchema(); } /** Register the table's schema */ function registerSchema() internal { - CallbackArray_.registerSchema(tableId); + AddressArray_.registerSchema(tableId); } function registerSchema(IStore store) internal { - CallbackArray_.registerSchema(tableId, store); + AddressArray_.registerSchema(tableId, store); } /** Set the table's data */ - function set(bytes32 key, bytes24[] memory callbacks) internal { - CallbackArray_.set(tableId, key, callbacks); + function set(bytes32 key, address[] memory addresses) internal { + AddressArray_.set(tableId, key, addresses); } /** - * Push an element to the callbacks array + * Push an element to the addresses array */ - function push(bytes32 key, bytes24 callback) internal { - CallbackArray_.push(tableId, key, callback); + function push(bytes32 key, address callback) internal { + AddressArray_.push(tableId, key, callback); } /** Get the table's data */ - function get(bytes32 key) internal view returns (bytes24[] memory callbacks) { - return CallbackArray_.get(tableId, key); + function get(bytes32 key) internal view returns (address[] memory addresses) { + return AddressArray_.get(tableId, key); } - function get(IStore store, bytes32 key) internal view returns (bytes24[] memory callbacks) { - return CallbackArray_.get(tableId, store, key); + function get(IStore store, bytes32 key) internal view returns (address[] memory addresses) { + return AddressArray_.get(tableId, store, key); } } diff --git a/packages/solecs/v2/tables/OnUpdateRecordHookTable.sol b/packages/solecs/v2/tables/OnUpdateRecordHookTable.sol deleted file mode 100644 index 06134c859e..0000000000 --- a/packages/solecs/v2/tables/OnUpdateRecordHookTable.sol +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.8.0; - -import { console } from "forge-std/console.sol"; -import { IStore } from "../IStore.sol"; -import { StoreSwitch } from "../StoreSwitch.sol"; -import { StoreCore } from "../StoreCore.sol"; -import { SchemaType } from "../Types.sol"; -import { Bytes } from "../Bytes.sol"; -import { Schema, Schema_ } from "../Schema.sol"; -import { PackedCounter, PackedCounter_ } from "../PackedCounter.sol"; -import { CallbackArray, CallbackArray_ } from "../schemas/CallbackArray.sol"; - -// -- User defined schema and tableId -- -bytes32 constant tableId = keccak256("mud.store.table.onUpdateRecordHook"); - -// -- Autogenerated library to interact with tables with this schema -- -// TODO: autogenerate - -library OnUpdateRecordHookTable { - /** Get the table's schema */ - function getSchema() internal pure returns (Schema schema) { - return CallbackArray_.getSchema(); - } - - /** Register the table's schema */ - function registerSchema() internal { - CallbackArray_.registerSchema(tableId); - } - - function registerSchema(IStore store) internal { - CallbackArray_.registerSchema(tableId, store); - } - - /** Set the table's data */ - function set(bytes32 key, bytes24[] memory callbacks) internal { - CallbackArray_.set(tableId, key, callbacks); - } - - /** - * Push an element to the callbacks array - */ - function push(bytes32 key, bytes24 callback) internal { - CallbackArray_.push(tableId, key, callback); - } - - /** Get the table's data */ - function get(bytes32 key) internal view returns (bytes24[] memory callbacks) { - return CallbackArray_.get(tableId, key); - } - - function get(IStore store, bytes32 key) internal view returns (bytes24[] memory callbacks) { - return CallbackArray_.get(tableId, store, key); - } -} diff --git a/packages/solecs/v2/test/StoreCore.t.sol b/packages/solecs/v2/test/StoreCore.t.sol index 867509d9e4..27678893fa 100644 --- a/packages/solecs/v2/test/StoreCore.t.sol +++ b/packages/solecs/v2/test/StoreCore.t.sol @@ -13,6 +13,9 @@ import { Cast } from "../Cast.sol"; import { Buffer, Buffer_ } from "../Buffer.sol"; import { Schema, Schema_ } from "../Schema.sol"; import { PackedCounter, PackedCounter_ } from "../PackedCounter.sol"; +import { StoreView } from "../StoreView.sol"; +import { IStore, IOnUpdateHook } from "../IStore.sol"; +import { StoreSwitch } from "../StoreSwitch.sol"; struct TestStruct { uint128 firstData; @@ -20,10 +23,35 @@ struct TestStruct { uint32[] thirdData; } -contract StoreCoreTest is DSTestPlus { +contract StoreCoreTest is DSTestPlus, StoreView { TestStruct private testStruct; + mapping(uint256 => bytes) private testMapping; + // Expose an external setRecord function for testing purposes of indexers (see testOnUpdateHook) + function setRecord( + bytes32 table, + bytes32[] memory key, + bytes memory data + ) public override { + StoreCore.setRecord(table, key, data); + } + + // Expose an external setField function for testing purposes of indexers (see testOnUpdateHook) + function setField( + bytes32 table, + bytes32[] memory key, + uint8 schemaIndex, + bytes memory data + ) public override { + StoreCore.setField(table, key, schemaIndex, data); + } + + // Expose an external registerSchema function for testing purposes of indexers (see testOnUpdateHook) + function registerSchema(bytes32 table, Schema schema) public override { + StoreCore.registerSchema(table, schema); + } + function testRegisterAndGetSchema() public { Schema schema = Schema_.encode(SchemaType.Uint8, SchemaType.Uint16, SchemaType.Uint8, SchemaType.Uint16); @@ -238,6 +266,7 @@ contract StoreCoreTest is DSTestPlus { // Set data uint256 gas = gasleft(); StoreCore.setRecord(table, key, data); + gas = gas - gasleft(); console.log("gas used (store complex struct / StoreCore): %s", gas); @@ -469,4 +498,135 @@ contract StoreCoreTest is DSTestPlus { loadedData = StoreCore.getRecord(table, key); assertEq(keccak256(loadedData), keccak256(new bytes(schema.staticDataLength()))); } + + function testAccessEmptyData() public { + bytes32 table = keccak256("some.table"); + Schema schema = Schema_.encode(SchemaType.Uint32, SchemaType.Uint32Array); + + StoreCore.registerSchema(table, schema); + + // Create key + bytes32[] memory key = new bytes32[](1); + key[0] = bytes32("some.key"); + + // !gasreport access non-existing record + bytes memory data1 = StoreCore.getRecord(table, key); + assertEq(data1.length, schema.staticDataLength()); + + // !gasreport access static field of non-existing record + bytes memory data2 = StoreCore.getField(table, key, 0); + assertEq(data2.length, schema.staticDataLength()); + + // !gasreport access dynamic field of non-existing record + bytes memory data3 = StoreCore.getField(table, key, 1); + assertEq(data3.length, 0); + } + + function testOnUpdateHook() public { + bytes32 table = keccak256("some.table"); + bytes32[] memory key = new bytes32[](1); + key[0] = keccak256("some key"); + + // Register table's schema + Schema schema = Schema_.encode(SchemaType.Uint128); + StoreCore.registerSchema(table, schema); + + // Create subscriber + MirrorSubscriber subscriber = new MirrorSubscriber(table, schema); + + // !gasreport register subscriber + StoreCore.registerOnUpdateHook(table, subscriber); + + bytes memory data = bytes.concat(bytes16(0x0102030405060708090a0b0c0d0e0f10)); + + // !gasreport set record on table with subscriber + StoreCore.setRecord(table, key, data); + + // Get data from indexed table - the indexer should have mirrored the data there + bytes memory indexedData = StoreCore.getRecord(indexerTableId, key); + assertEq(keccak256(data), keccak256(indexedData)); + + data = bytes.concat(bytes16(0x1112131415161718191a1b1c1d1e1f20)); + + // !gasreport set field on table with subscriber + StoreCore.setField(table, key, 0, data); + + // Get data from indexed table - the indexer should have mirrored the data there + indexedData = StoreCore.getRecord(indexerTableId, key); + assertEq(keccak256(data), keccak256(indexedData)); + } + + function testOnUpdateHookDynamicData() public { + bytes32 table = keccak256("some.table"); + bytes32[] memory key = new bytes32[](1); + key[0] = keccak256("some key"); + + // Register table's schema + Schema schema = Schema_.encode(SchemaType.Uint128, SchemaType.Uint32Array); + StoreCore.registerSchema(table, schema); + + // Create subscriber + MirrorSubscriber subscriber = new MirrorSubscriber(table, schema); + + // !gasreport register subscriber + StoreCore.registerOnUpdateHook(table, subscriber); + + uint32[] memory arrayData = new uint32[](1); + arrayData[0] = 0x01020304; + bytes memory arrayDataBytes = Bytes.from(arrayData); + PackedCounter encodedArrayDataLength = PackedCounter_.pack(uint16(arrayDataBytes.length)); + bytes memory dynamicData = bytes.concat(encodedArrayDataLength.unwrap(), arrayDataBytes); + bytes memory staticData = bytes.concat(bytes16(0x0102030405060708090a0b0c0d0e0f10)); + bytes memory data = bytes.concat(staticData, dynamicData); + + // !gasreport set record on table with subscriber + StoreCore.setRecord(table, key, data); + + // Get data from indexed table - the indexer should have mirrored the data there + bytes memory indexedData = StoreCore.getRecord(indexerTableId, key); + assertEq(keccak256(data), keccak256(indexedData)); + + // Update dynamic data + arrayData[0] = 0x11121314; + arrayDataBytes = Bytes.from(arrayData); + dynamicData = bytes.concat(encodedArrayDataLength.unwrap(), arrayDataBytes); + data = bytes.concat(staticData, dynamicData); + + // !gasreport set field on table with subscriber + StoreCore.setField(table, key, 1, arrayDataBytes); + + // Get data from indexed table - the indexer should have mirrored the data there + indexedData = StoreCore.getRecord(indexerTableId, key); + assertEq(keccak256(data), keccak256(indexedData)); + } +} + +bytes32 constant indexerTableId = keccak256("indexer.table"); + +contract MirrorSubscriber is IOnUpdateHook { + bytes32 _table; + + constructor(bytes32 table, Schema schema) { + IStore(msg.sender).registerSchema(indexerTableId, schema); + _table = table; + } + + function onUpdateRecord( + bytes32 table, + bytes32[] memory key, + bytes memory data + ) public { + if (table != table) revert("invalid table"); + StoreSwitch.setRecord(indexerTableId, key, data); + } + + function onUpdateField( + bytes32 table, + bytes32[] memory key, + uint8 schemaIndex, + bytes memory data + ) public { + if (table != table) revert("invalid table"); + StoreSwitch.setField(indexerTableId, key, schemaIndex, data); + } } diff --git a/packages/solecs/v2/test/schemas/AddressArray.t.sol b/packages/solecs/v2/test/schemas/AddressArray.t.sol new file mode 100644 index 0000000000..e93201c2b3 --- /dev/null +++ b/packages/solecs/v2/test/schemas/AddressArray.t.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import { console } from "forge-std/console.sol"; +import { DSTestPlus } from "solmate/test/utils/DSTestPlus.sol"; +import { StoreView } from "../../StoreView.sol"; +import { AddressArray, AddressArray_ } from "../../schemas/AddressArray.sol"; + +bytes32 constant tableId = keccak256("mud.store.table.addressArray"); + +contract AddressArrayTest is DSTestPlus, StoreView { + function testSetAndGet() public { + AddressArray_.registerSchema(tableId); + bytes32 key = keccak256("somekey"); + + address[] memory addresses = new address[](1); + addresses[0] = address(this); + + // !gasreport set record in AddressArrayTable + AddressArray_.set(tableId, key, addresses); + + // !gasreport get record from AddressArrayTable (warm) + address[] memory returnedAddresses = AddressArray_.get(tableId, key); + + assertEq(returnedAddresses.length, addresses.length); + assertEq(returnedAddresses[0], addresses[0]); + + // !gasreport push record to AddressArrayTable + AddressArray_.push(tableId, key, addresses[0]); + + returnedAddresses = AddressArray_.get(tableId, key); + + assertEq(returnedAddresses.length, 2); + assertEq(returnedAddresses[1], addresses[0]); + } +} From 735921afa00ffd9fcae805e6b28da2f0e685c38c Mon Sep 17 00:00:00 2001 From: alvrs Date: Tue, 31 Jan 2023 21:32:34 +0100 Subject: [PATCH 32/82] feat(solecs): wip data model - add registerOnUpdateHook to interface --- packages/solecs/v2/IStore.sol | 3 +++ packages/solecs/v2/StoreCore.sol | 1 + packages/solecs/v2/StoreView.sol | 21 ++------------------- 3 files changed, 6 insertions(+), 19 deletions(-) diff --git a/packages/solecs/v2/IStore.sol b/packages/solecs/v2/IStore.sol index d815c69bc2..6102db890e 100644 --- a/packages/solecs/v2/IStore.sol +++ b/packages/solecs/v2/IStore.sol @@ -35,6 +35,9 @@ interface IStore { bytes memory data ) external; + // Register a callback to be called when a record is updated + function registerOnUpdateHook(bytes32 table, IOnUpdateHook onUpdateHook) external; + // Get full record (including full array, load table schema from storage) function getRecord(bytes32 table, bytes32[] memory key) external view returns (bytes memory data); diff --git a/packages/solecs/v2/StoreCore.sol b/packages/solecs/v2/StoreCore.sol index 6e6ee9411a..5a6f064a54 100644 --- a/packages/solecs/v2/StoreCore.sol +++ b/packages/solecs/v2/StoreCore.sol @@ -16,6 +16,7 @@ import { IOnUpdateHook } from "./IStore.sol"; // - Turn all storage pointer to uint256 for consistency (uint256 is better than bytes32 because it's easier to do arithmetic on) // - Change Storage library functions to make it clearer which argument is offset and which is length // - Streamline naming in Storage and Memory libraries (probably just use load and store instead of read and write?) +// - Move all internal functions to a separate StoreCoreInternal library to discourage using it 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 diff --git a/packages/solecs/v2/StoreView.sol b/packages/solecs/v2/StoreView.sol index 8234d0bd6d..cccb468812 100644 --- a/packages/solecs/v2/StoreView.sol +++ b/packages/solecs/v2/StoreView.sol @@ -2,7 +2,7 @@ pragma solidity >=0.8.0; import { SchemaType } from "./Types.sol"; -import { IStore } from "./IStore.sol"; +import { IStore, IOnUpdateHook } from "./IStore.sol"; import { StoreCore } from "./StoreCore.sol"; import { Schema } from "./Schema.sol"; @@ -48,24 +48,7 @@ contract StoreView is IStore { revert Store_BaseContractNotImplemented(); } - // Set full record of a single item at a given array index - function setArrayIndex( - bytes32, - bytes32[] memory, - uint16, - bytes memory - ) public virtual { - revert Store_BaseContractNotImplemented(); - } - - // Set partial data of a single item at a given array index - function setArrayIndexField( - bytes32, - bytes32[] memory, - uint16, - uint8, - bytes memory - ) public virtual { + function registerOnUpdateHook(bytes32, IOnUpdateHook) public virtual { revert Store_BaseContractNotImplemented(); } From 2cb36b7c31968f368caea3e72f4ea0f6ee225e75 Mon Sep 17 00:00:00 2001 From: alvrs Date: Tue, 31 Jan 2023 21:44:22 +0100 Subject: [PATCH 33/82] feat(store): move store core source into own package outside of solecs --- package.json | 1 + packages/solecs/foundry.toml | 2 +- packages/store/.gitignore | 10 + packages/store/.npmignore | 7 + packages/store/.nvmrc | 1 + packages/store/.prettierrc | 7 + packages/store/.solhint.json | 8 + packages/store/CHANGELOG.md | 4 + packages/store/CHANGELOG.yml | 1 + packages/store/README.md | 1 + packages/store/foundry.toml | 9 + packages/store/gas-report.txt | 21 + packages/store/git-install.sh | 5 + packages/store/hardhat.config.ts | 23 + packages/store/index.ts | 1 + packages/store/index.yml | 2 + packages/store/package.json | 46 + packages/store/remappings.txt | 2 + packages/{solecs/v2 => store/src}/Buffer.sol | 0 packages/{solecs/v2 => store/src}/Bytes.sol | 0 packages/{solecs/v2 => store/src}/Cast.sol | 0 packages/{solecs/v2 => store/src}/IStore.sol | 0 packages/{solecs/v2 => store/src}/Memory.sol | 0 .../v2 => store/src}/PackedCounter.sol | 0 packages/{solecs/v2 => store/src}/Schema.sol | 0 packages/{solecs/v2 => store/src}/Storage.sol | 0 .../{solecs/v2 => store/src}/Storage2.sol | 0 .../{solecs/v2 => store/src}/StoreCore.sol | 0 .../{solecs/v2 => store/src}/StoreSwitch.sol | 0 .../{solecs/v2 => store/src}/StoreView.sol | 0 packages/{solecs/v2 => store/src}/System.sol | 0 packages/{solecs/v2 => store/src}/Types.sol | 0 packages/{solecs/v2 => store/src}/Utils.sol | 0 packages/{solecs/v2 => store/src}/World.sol | 0 .../v2 => store/src}/schemas/AddressArray.sol | 0 .../src}/schemas/CallbackArray.sol | 0 .../v2 => store/src}/schemas/Mixed.sol | 0 .../v2 => store/src}/schemas/Route.sol | 0 .../v2 => store/src}/schemas/Vector2.sol | 0 .../v2 => store/src}/tables/MixedTable.sol | 0 .../src}/tables/OnUpdateHookTable.sol | 0 .../v2 => store/src}/tables/RouteTable.sol | 0 .../v2 => store/src}/tables/Vector2Table.sol | 0 .../v2 => store/src}/test/Buffer.t.sol | 0 .../{solecs/v2 => store/src}/test/Bytes.t.sol | 0 .../{solecs/v2 => store/src}/test/Gas.t.sol | 0 .../v2 => store/src}/test/MixedTable.t.sol | 0 .../v2 => store/src}/test/PackedCounter.t.sol | 0 .../v2 => store/src}/test/RouteTable.t.sol | 0 .../v2 => store/src}/test/Schema.t.sol | 0 .../v2 => store/src}/test/Storage.t.sol | 0 .../v2 => store/src}/test/StoreCore.t.sol | 0 .../v2 => store/src}/test/StoreSwitch.t.sol | 0 .../v2 => store/src}/test/System.t.sol | 0 .../v2 => store/src}/test/Vector2Table.t.sol | 0 .../{solecs/v2 => store/src}/test/World.t.sol | 0 .../src}/test/schemas/AddressArray.t.sol | 0 .../src}/test/schemas/CallbackArray.t.sol | 0 packages/store/tasks/compile.ts | 38 + packages/store/tsconfig.json | 102 + packages/store/yarn.lock | 3309 +++++++++++++++++ 61 files changed, 3599 insertions(+), 1 deletion(-) create mode 100644 packages/store/.gitignore create mode 100644 packages/store/.npmignore create mode 100644 packages/store/.nvmrc create mode 100644 packages/store/.prettierrc create mode 100644 packages/store/.solhint.json create mode 100644 packages/store/CHANGELOG.md create mode 100644 packages/store/CHANGELOG.yml create mode 100644 packages/store/README.md create mode 100644 packages/store/foundry.toml create mode 100644 packages/store/gas-report.txt create mode 100755 packages/store/git-install.sh create mode 100644 packages/store/hardhat.config.ts create mode 100644 packages/store/index.ts create mode 100644 packages/store/index.yml create mode 100644 packages/store/package.json create mode 100644 packages/store/remappings.txt rename packages/{solecs/v2 => store/src}/Buffer.sol (100%) rename packages/{solecs/v2 => store/src}/Bytes.sol (100%) rename packages/{solecs/v2 => store/src}/Cast.sol (100%) rename packages/{solecs/v2 => store/src}/IStore.sol (100%) rename packages/{solecs/v2 => store/src}/Memory.sol (100%) rename packages/{solecs/v2 => store/src}/PackedCounter.sol (100%) rename packages/{solecs/v2 => store/src}/Schema.sol (100%) rename packages/{solecs/v2 => store/src}/Storage.sol (100%) rename packages/{solecs/v2 => store/src}/Storage2.sol (100%) rename packages/{solecs/v2 => store/src}/StoreCore.sol (100%) rename packages/{solecs/v2 => store/src}/StoreSwitch.sol (100%) rename packages/{solecs/v2 => store/src}/StoreView.sol (100%) rename packages/{solecs/v2 => store/src}/System.sol (100%) rename packages/{solecs/v2 => store/src}/Types.sol (100%) rename packages/{solecs/v2 => store/src}/Utils.sol (100%) rename packages/{solecs/v2 => store/src}/World.sol (100%) rename packages/{solecs/v2 => store/src}/schemas/AddressArray.sol (100%) rename packages/{solecs/v2 => store/src}/schemas/CallbackArray.sol (100%) rename packages/{solecs/v2 => store/src}/schemas/Mixed.sol (100%) rename packages/{solecs/v2 => store/src}/schemas/Route.sol (100%) rename packages/{solecs/v2 => store/src}/schemas/Vector2.sol (100%) rename packages/{solecs/v2 => store/src}/tables/MixedTable.sol (100%) rename packages/{solecs/v2 => store/src}/tables/OnUpdateHookTable.sol (100%) rename packages/{solecs/v2 => store/src}/tables/RouteTable.sol (100%) rename packages/{solecs/v2 => store/src}/tables/Vector2Table.sol (100%) rename packages/{solecs/v2 => store/src}/test/Buffer.t.sol (100%) rename packages/{solecs/v2 => store/src}/test/Bytes.t.sol (100%) rename packages/{solecs/v2 => store/src}/test/Gas.t.sol (100%) rename packages/{solecs/v2 => store/src}/test/MixedTable.t.sol (100%) rename packages/{solecs/v2 => store/src}/test/PackedCounter.t.sol (100%) rename packages/{solecs/v2 => store/src}/test/RouteTable.t.sol (100%) rename packages/{solecs/v2 => store/src}/test/Schema.t.sol (100%) rename packages/{solecs/v2 => store/src}/test/Storage.t.sol (100%) rename packages/{solecs/v2 => store/src}/test/StoreCore.t.sol (100%) rename packages/{solecs/v2 => store/src}/test/StoreSwitch.t.sol (100%) rename packages/{solecs/v2 => store/src}/test/System.t.sol (100%) rename packages/{solecs/v2 => store/src}/test/Vector2Table.t.sol (100%) rename packages/{solecs/v2 => store/src}/test/World.t.sol (100%) rename packages/{solecs/v2 => store/src}/test/schemas/AddressArray.t.sol (100%) rename packages/{solecs/v2 => store/src}/test/schemas/CallbackArray.t.sol (100%) create mode 100644 packages/store/tasks/compile.ts create mode 100644 packages/store/tsconfig.json create mode 100644 packages/store/yarn.lock diff --git a/package.json b/package.json index 278a442505..9e4865824f 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "packages": [ "packages/utils", "packages/solecs", + "packages/store", "packages/cli", "packages/recs", "packages/react", diff --git a/packages/solecs/foundry.toml b/packages/solecs/foundry.toml index 19294352cb..4e1a99ebba 100644 --- a/packages/solecs/foundry.toml +++ b/packages/solecs/foundry.toml @@ -5,5 +5,5 @@ optimizer = true optimizer_runs = 1000000 verbosity = 1 libs = ["../../node_modules"] -src = "v2" +src = "src" out = "out" \ No newline at end of file diff --git a/packages/store/.gitignore b/packages/store/.gitignore new file mode 100644 index 0000000000..2f6f3adfc2 --- /dev/null +++ b/packages/store/.gitignore @@ -0,0 +1,10 @@ +cache +abi +out +types/ethers-contracts +docs +_docs +DOCS.md +artifacts +yarn-error.log +API \ No newline at end of file diff --git a/packages/store/.npmignore b/packages/store/.npmignore new file mode 100644 index 0000000000..4240dbeb28 --- /dev/null +++ b/packages/store/.npmignore @@ -0,0 +1,7 @@ +* + +!abi/** +!src/** +!types/** +!package.json +!README.md \ No newline at end of file diff --git a/packages/store/.nvmrc b/packages/store/.nvmrc new file mode 100644 index 0000000000..8e2afd3427 --- /dev/null +++ b/packages/store/.nvmrc @@ -0,0 +1 @@ +17 \ No newline at end of file diff --git a/packages/store/.prettierrc b/packages/store/.prettierrc new file mode 100644 index 0000000000..9a5a009900 --- /dev/null +++ b/packages/store/.prettierrc @@ -0,0 +1,7 @@ +{ + "printWidth": 120, + "semi": true, + "tabWidth": 2, + "useTabs": false, + "bracketSpacing": true +} diff --git a/packages/store/.solhint.json b/packages/store/.solhint.json new file mode 100644 index 0000000000..4e2baa8be7 --- /dev/null +++ b/packages/store/.solhint.json @@ -0,0 +1,8 @@ +{ + "extends": "solhint:recommended", + "rules": { + "compiler-version": ["error", ">=0.8.0"], + "avoid-low-level-calls": "off", + "func-visibility": ["warn", { "ignoreConstructors": true }] + } +} diff --git a/packages/store/CHANGELOG.md b/packages/store/CHANGELOG.md new file mode 100644 index 0000000000..e4d87c4d45 --- /dev/null +++ b/packages/store/CHANGELOG.md @@ -0,0 +1,4 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. diff --git a/packages/store/CHANGELOG.yml b/packages/store/CHANGELOG.yml new file mode 100644 index 0000000000..e3f6e391b4 --- /dev/null +++ b/packages/store/CHANGELOG.yml @@ -0,0 +1 @@ +label: Changelog diff --git a/packages/store/README.md b/packages/store/README.md new file mode 100644 index 0000000000..3f8815b136 --- /dev/null +++ b/packages/store/README.md @@ -0,0 +1 @@ +# Store diff --git a/packages/store/foundry.toml b/packages/store/foundry.toml new file mode 100644 index 0000000000..4e1a99ebba --- /dev/null +++ b/packages/store/foundry.toml @@ -0,0 +1,9 @@ +[profile.default] +ffi = false +fuzz_runs = 256 +optimizer = true +optimizer_runs = 1000000 +verbosity = 1 +libs = ["../../node_modules"] +src = "src" +out = "out" \ No newline at end of file diff --git a/packages/store/gas-report.txt b/packages/store/gas-report.txt new file mode 100644 index 0000000000..d9df7b16e0 --- /dev/null +++ b/packages/store/gas-report.txt @@ -0,0 +1,21 @@ +(v2/test/Buffer.t.sol) | allocate a buffer [Buffer buf = Buffer_.allocate(32)]: 73 +(v2/test/Buffer.t.sol) | get buffer length [buf.length()]: 87 +(v2/test/Buffer.t.sol) | get buffer pointer [buf.ptr()]: 33 +(v2/test/Buffer.t.sol) | get buffer capacity [buf.capacity()]: 7 +(v2/test/Buffer.t.sol) | append unchecked bytes memory (8) to buffer [buf.appendUnchecked(data1)]: 478 +(v2/test/Buffer.t.sol) | append bytes memory (8) to buffer [buf.append(data2)]: 773 +(v2/test/Buffer.t.sol) | append unchecked bytes8 of bytes32 to buffer [buf.appendUnchecked(data1, 8)]: 351 +(v2/test/Buffer.t.sol) | append bytes8 of bytes32 to buffer [buf.append(data2, 8)]: 645 +(v2/test/Buffer.t.sol) | concat 3 bytes memory (32) using buffer [Buffer buf = Buffer_.concat(data1, data2, data3)]: 2638 +(v2/test/Buffer.t.sol) | concat 3 bytes memory (32) using bytes.concat [bytes memory concat = bytes.concat(data1, data2, data3)]: 641 +(v2/test/Buffer.t.sol) | create a buffer from 8 bytes [Buffer buf = Buffer_.fromBytes(data)]: 40 +(v2/test/Buffer.t.sol) | read bytes32 from buffer [bytes32 value = buf.read32(4)]: 102 +(v2/test/Buffer.t.sol) | read bytes8 with offset 3 from buffer [bytes8 value2 = buf.read8(3)]: 151 +(v2/test/Buffer.t.sol) | read bytes1 with offset 7 from buffer [bytes1 value3 = buf.read1(7)]: 151 +(v2/test/Buffer.t.sol) | set buffer length unchecked [buf._setLengthUnchecked(8)]: 86 +(v2/test/Buffer.t.sol) | set buffer length [buf._setLength(16)]: 192 +(v2/test/Buffer.t.sol) | slice 4 bytes from buffer with offset 4 [bytes memory slice = buf.slice(4, 4)]: 635 +(v2/test/Buffer.t.sol) | convert array pointer to uint256[] [uint256[] memory arr = Cast.toUint256Array(arrayPtr)]: 10 +(v2/test/Buffer.t.sol) | buffer toArray with element length 4 [uint256 arrayPtr = buf.toArray(4)]: 663 +(v2/test/Buffer.t.sol) | convert array pointer to uint32[] [uint32[] memory arr = Cast.toUint32Array(arrayPtr)]: 10 +(v2/test/Buffer.t.sol) | buffer (32 bytes) to bytes memory [bytes memory bufferData = buf.toBytes()]: 90 \ No newline at end of file diff --git a/packages/store/git-install.sh b/packages/store/git-install.sh new file mode 100755 index 0000000000..b86de4f8c9 --- /dev/null +++ b/packages/store/git-install.sh @@ -0,0 +1,5 @@ +#! usr/bin/bash +giturl=https://github.com/$1.git +head=$(git ls-remote $giturl HEAD | head -n1 | awk '{print $1;}') +yarn add $giturl#$head +echo "Installed $giturl#$head" \ No newline at end of file diff --git a/packages/store/hardhat.config.ts b/packages/store/hardhat.config.ts new file mode 100644 index 0000000000..da00b249c6 --- /dev/null +++ b/packages/store/hardhat.config.ts @@ -0,0 +1,23 @@ +import { HardhatUserConfig } from "hardhat/config"; +import "./tasks/compile"; +import "solidity-docgen"; + +const config: HardhatUserConfig = { + paths: { + sources: "./src", + }, + solidity: { + version: "0.8.13", + settings: { + optimizer: { + enabled: true, + runs: 200, + }, + }, + }, + docgen: { + outputDir: "API", + }, +}; + +export default config; diff --git a/packages/store/index.ts b/packages/store/index.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/packages/store/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/packages/store/index.yml b/packages/store/index.yml new file mode 100644 index 0000000000..67be39bdfd --- /dev/null +++ b/packages/store/index.yml @@ -0,0 +1,2 @@ +label: store +order: 50 diff --git a/packages/store/package.json b/packages/store/package.json new file mode 100644 index 0000000000..175ffc4d1c --- /dev/null +++ b/packages/store/package.json @@ -0,0 +1,46 @@ +{ + "name": "@latticexyz/store", + "license": "MIT", + "version": "1.33.1", + "description": "Store", + "types": "./types/ethers-contracts/", + "repository": { + "type": "git", + "url": "https://github.com/latticexyz/mud.git", + "directory": "packages/store" + }, + "scripts": { + "prepare": "yarn build && chmod u+x git-install.sh", + "git:install": "bash git-install.sh", + "test": "forge test", + "build": "rimraf out && forge build --out out && yarn dist && yarn types", + "dist": "rimraf abi && mkdir abi && cp out/*.sol/*.json abi/ && yarn rimraf abi/*.metadata.json", + "types": "rimraf types && typechain --target=ethers-v5 abi/*.json", + "prettier": "prettier --write 'src/**/*.sol'", + "solhint": "solhint --config ./.solhint.json 'src/**/*.sol'", + "lint": "yarn prettier && yarn solhint", + "link": "yarn link", + "docs": "rimraf API && hardhat docgen && echo 'label: API\norder: 50' > API/index.yml", + "release": "npm publish || echo 'version already published'" + }, + "devDependencies": { + "@typechain/ethers-v5": "^9.0.0", + "@types/mocha": "^9.1.1", + "ds-test": "https://github.com/dapphub/ds-test.git#c7a36fb236f298e04edf28e2fee385b80f53945f", + "forge-std": "https://github.com/foundry-rs/forge-std.git#37a3fe48c3a4d8239cda93445f0b5e76b1507436", + "hardhat": "^2.10.1", + "prettier": "^2.6.2", + "prettier-plugin-solidity": "^1.0.0-beta.19", + "rimraf": "^3.0.2", + "solhint": "^3.3.7", + "solidity-docgen": "^0.6.0-beta.22", + "ts-node": "10.7", + "typechain": "^8.1.1", + "typedoc": "^0.23.10" + }, + "peerDependencies": { + "ds-test": "https://github.com/dapphub/ds-test.git#c7a36fb236f298e04edf28e2fee385b80f53945f", + "forge-std": "https://github.com/foundry-rs/forge-std.git#37a3fe48c3a4d8239cda93445f0b5e76b1507436" + }, + "gitHead": "218f56893d268b0c5157a3e4c603b859e287a343" +} diff --git a/packages/store/remappings.txt b/packages/store/remappings.txt new file mode 100644 index 0000000000..aeeced7eb6 --- /dev/null +++ b/packages/store/remappings.txt @@ -0,0 +1,2 @@ +ds-test/=../../node_modules/ds-test/src/ +forge-std/=../../node_modules/forge-std/src/ diff --git a/packages/solecs/v2/Buffer.sol b/packages/store/src/Buffer.sol similarity index 100% rename from packages/solecs/v2/Buffer.sol rename to packages/store/src/Buffer.sol diff --git a/packages/solecs/v2/Bytes.sol b/packages/store/src/Bytes.sol similarity index 100% rename from packages/solecs/v2/Bytes.sol rename to packages/store/src/Bytes.sol diff --git a/packages/solecs/v2/Cast.sol b/packages/store/src/Cast.sol similarity index 100% rename from packages/solecs/v2/Cast.sol rename to packages/store/src/Cast.sol diff --git a/packages/solecs/v2/IStore.sol b/packages/store/src/IStore.sol similarity index 100% rename from packages/solecs/v2/IStore.sol rename to packages/store/src/IStore.sol diff --git a/packages/solecs/v2/Memory.sol b/packages/store/src/Memory.sol similarity index 100% rename from packages/solecs/v2/Memory.sol rename to packages/store/src/Memory.sol diff --git a/packages/solecs/v2/PackedCounter.sol b/packages/store/src/PackedCounter.sol similarity index 100% rename from packages/solecs/v2/PackedCounter.sol rename to packages/store/src/PackedCounter.sol diff --git a/packages/solecs/v2/Schema.sol b/packages/store/src/Schema.sol similarity index 100% rename from packages/solecs/v2/Schema.sol rename to packages/store/src/Schema.sol diff --git a/packages/solecs/v2/Storage.sol b/packages/store/src/Storage.sol similarity index 100% rename from packages/solecs/v2/Storage.sol rename to packages/store/src/Storage.sol diff --git a/packages/solecs/v2/Storage2.sol b/packages/store/src/Storage2.sol similarity index 100% rename from packages/solecs/v2/Storage2.sol rename to packages/store/src/Storage2.sol diff --git a/packages/solecs/v2/StoreCore.sol b/packages/store/src/StoreCore.sol similarity index 100% rename from packages/solecs/v2/StoreCore.sol rename to packages/store/src/StoreCore.sol diff --git a/packages/solecs/v2/StoreSwitch.sol b/packages/store/src/StoreSwitch.sol similarity index 100% rename from packages/solecs/v2/StoreSwitch.sol rename to packages/store/src/StoreSwitch.sol diff --git a/packages/solecs/v2/StoreView.sol b/packages/store/src/StoreView.sol similarity index 100% rename from packages/solecs/v2/StoreView.sol rename to packages/store/src/StoreView.sol diff --git a/packages/solecs/v2/System.sol b/packages/store/src/System.sol similarity index 100% rename from packages/solecs/v2/System.sol rename to packages/store/src/System.sol diff --git a/packages/solecs/v2/Types.sol b/packages/store/src/Types.sol similarity index 100% rename from packages/solecs/v2/Types.sol rename to packages/store/src/Types.sol diff --git a/packages/solecs/v2/Utils.sol b/packages/store/src/Utils.sol similarity index 100% rename from packages/solecs/v2/Utils.sol rename to packages/store/src/Utils.sol diff --git a/packages/solecs/v2/World.sol b/packages/store/src/World.sol similarity index 100% rename from packages/solecs/v2/World.sol rename to packages/store/src/World.sol diff --git a/packages/solecs/v2/schemas/AddressArray.sol b/packages/store/src/schemas/AddressArray.sol similarity index 100% rename from packages/solecs/v2/schemas/AddressArray.sol rename to packages/store/src/schemas/AddressArray.sol diff --git a/packages/solecs/v2/schemas/CallbackArray.sol b/packages/store/src/schemas/CallbackArray.sol similarity index 100% rename from packages/solecs/v2/schemas/CallbackArray.sol rename to packages/store/src/schemas/CallbackArray.sol diff --git a/packages/solecs/v2/schemas/Mixed.sol b/packages/store/src/schemas/Mixed.sol similarity index 100% rename from packages/solecs/v2/schemas/Mixed.sol rename to packages/store/src/schemas/Mixed.sol diff --git a/packages/solecs/v2/schemas/Route.sol b/packages/store/src/schemas/Route.sol similarity index 100% rename from packages/solecs/v2/schemas/Route.sol rename to packages/store/src/schemas/Route.sol diff --git a/packages/solecs/v2/schemas/Vector2.sol b/packages/store/src/schemas/Vector2.sol similarity index 100% rename from packages/solecs/v2/schemas/Vector2.sol rename to packages/store/src/schemas/Vector2.sol diff --git a/packages/solecs/v2/tables/MixedTable.sol b/packages/store/src/tables/MixedTable.sol similarity index 100% rename from packages/solecs/v2/tables/MixedTable.sol rename to packages/store/src/tables/MixedTable.sol diff --git a/packages/solecs/v2/tables/OnUpdateHookTable.sol b/packages/store/src/tables/OnUpdateHookTable.sol similarity index 100% rename from packages/solecs/v2/tables/OnUpdateHookTable.sol rename to packages/store/src/tables/OnUpdateHookTable.sol diff --git a/packages/solecs/v2/tables/RouteTable.sol b/packages/store/src/tables/RouteTable.sol similarity index 100% rename from packages/solecs/v2/tables/RouteTable.sol rename to packages/store/src/tables/RouteTable.sol diff --git a/packages/solecs/v2/tables/Vector2Table.sol b/packages/store/src/tables/Vector2Table.sol similarity index 100% rename from packages/solecs/v2/tables/Vector2Table.sol rename to packages/store/src/tables/Vector2Table.sol diff --git a/packages/solecs/v2/test/Buffer.t.sol b/packages/store/src/test/Buffer.t.sol similarity index 100% rename from packages/solecs/v2/test/Buffer.t.sol rename to packages/store/src/test/Buffer.t.sol diff --git a/packages/solecs/v2/test/Bytes.t.sol b/packages/store/src/test/Bytes.t.sol similarity index 100% rename from packages/solecs/v2/test/Bytes.t.sol rename to packages/store/src/test/Bytes.t.sol diff --git a/packages/solecs/v2/test/Gas.t.sol b/packages/store/src/test/Gas.t.sol similarity index 100% rename from packages/solecs/v2/test/Gas.t.sol rename to packages/store/src/test/Gas.t.sol diff --git a/packages/solecs/v2/test/MixedTable.t.sol b/packages/store/src/test/MixedTable.t.sol similarity index 100% rename from packages/solecs/v2/test/MixedTable.t.sol rename to packages/store/src/test/MixedTable.t.sol diff --git a/packages/solecs/v2/test/PackedCounter.t.sol b/packages/store/src/test/PackedCounter.t.sol similarity index 100% rename from packages/solecs/v2/test/PackedCounter.t.sol rename to packages/store/src/test/PackedCounter.t.sol diff --git a/packages/solecs/v2/test/RouteTable.t.sol b/packages/store/src/test/RouteTable.t.sol similarity index 100% rename from packages/solecs/v2/test/RouteTable.t.sol rename to packages/store/src/test/RouteTable.t.sol diff --git a/packages/solecs/v2/test/Schema.t.sol b/packages/store/src/test/Schema.t.sol similarity index 100% rename from packages/solecs/v2/test/Schema.t.sol rename to packages/store/src/test/Schema.t.sol diff --git a/packages/solecs/v2/test/Storage.t.sol b/packages/store/src/test/Storage.t.sol similarity index 100% rename from packages/solecs/v2/test/Storage.t.sol rename to packages/store/src/test/Storage.t.sol diff --git a/packages/solecs/v2/test/StoreCore.t.sol b/packages/store/src/test/StoreCore.t.sol similarity index 100% rename from packages/solecs/v2/test/StoreCore.t.sol rename to packages/store/src/test/StoreCore.t.sol diff --git a/packages/solecs/v2/test/StoreSwitch.t.sol b/packages/store/src/test/StoreSwitch.t.sol similarity index 100% rename from packages/solecs/v2/test/StoreSwitch.t.sol rename to packages/store/src/test/StoreSwitch.t.sol diff --git a/packages/solecs/v2/test/System.t.sol b/packages/store/src/test/System.t.sol similarity index 100% rename from packages/solecs/v2/test/System.t.sol rename to packages/store/src/test/System.t.sol diff --git a/packages/solecs/v2/test/Vector2Table.t.sol b/packages/store/src/test/Vector2Table.t.sol similarity index 100% rename from packages/solecs/v2/test/Vector2Table.t.sol rename to packages/store/src/test/Vector2Table.t.sol diff --git a/packages/solecs/v2/test/World.t.sol b/packages/store/src/test/World.t.sol similarity index 100% rename from packages/solecs/v2/test/World.t.sol rename to packages/store/src/test/World.t.sol diff --git a/packages/solecs/v2/test/schemas/AddressArray.t.sol b/packages/store/src/test/schemas/AddressArray.t.sol similarity index 100% rename from packages/solecs/v2/test/schemas/AddressArray.t.sol rename to packages/store/src/test/schemas/AddressArray.t.sol diff --git a/packages/solecs/v2/test/schemas/CallbackArray.t.sol b/packages/store/src/test/schemas/CallbackArray.t.sol similarity index 100% rename from packages/solecs/v2/test/schemas/CallbackArray.t.sol rename to packages/store/src/test/schemas/CallbackArray.t.sol diff --git a/packages/store/tasks/compile.ts b/packages/store/tasks/compile.ts new file mode 100644 index 0000000000..65335021b4 --- /dev/null +++ b/packages/store/tasks/compile.ts @@ -0,0 +1,38 @@ +import { TASK_COMPILE_SOLIDITY } from "hardhat/builtin-tasks/task-names"; +import * as fs from "fs"; +import * as path from "path"; +import { subtask } from "hardhat/config"; + +subtask(TASK_COMPILE_SOLIDITY).setAction(async (_: { force: boolean; quiet: boolean }, { config }, runSuper) => { + console.log("Symlinking forge-style libraries"); + const symlinks: string[] = []; + const remappings = fs + .readFileSync("remappings.txt") + .toString() + .split("\n") + .filter((r) => r.length > 0) + .map((r) => r.split("=")); + + console.log("remappings", remappings); + for (const [library, libraryPath] of remappings) { + const symlinkPath = path.join(process.cwd(), library.replace("/", "")); + console.log("Adding symlink at path: " + symlinkPath); + if (fs.existsSync(symlinkPath)) { + console.warn("symlink already exists!"); + } else { + const libPath = path.join(config.paths.sources, "..", libraryPath); + fs.symlinkSync(libPath, symlinkPath, "dir"); + } + symlinks.push(symlinkPath); + } + try { + await runSuper(); + } catch (e) { + console.error(e); + } finally { + for (const symlink of symlinks) { + console.log("Removing symlink at path: " + symlink); + fs.unlinkSync(symlink); + } + } +}); diff --git a/packages/store/tsconfig.json b/packages/store/tsconfig.json new file mode 100644 index 0000000000..5587243470 --- /dev/null +++ b/packages/store/tsconfig.json @@ -0,0 +1,102 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + + /* Projects */ + // "incremental": true, /* Enable incremental compilation */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + + /* Language and Environment */ + "target": "es2019" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */ + // "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + + /* Modules */ + // "module": "esnext" /* Specify what module code is generated. */, + // "rootDir": "." /* Specify the root folder within your source files. */, + "moduleResolution": "node" /* Specify how TypeScript looks up a file from a given module specifier. */, + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */ + "types": ["mocha"] /* Specify type package names to be included without being referenced in a source file. */, + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "resolveJsonModule": true, /* Enable importing .json files */ + // "noResolve": true, /* Disallow `import`s, `require`s or ``s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */ + + /* Emit */ + // "declaration": true /* Generate .d.ts files from TypeScript and JavaScript files in your project. */, + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true /* Only output d.ts files and not JavaScript files. */, + // "sourceMap": true /* Create source map files for emitted JavaScript files. */, + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */ + // "outDir": "dist" /* Specify an output folder for all emitted files. */, + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ + + /* Interop Constraints */ + // "isolatedModules": true /* Ensure that each file can be safely transpiled without relying on other imports. */, + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */ + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + // "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */ + + /* Type Checking */ + // "strict": true /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */ + // "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */ + // "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + // "skipLibCheck": true /* Skip type checking all .d.ts files. */ + } + // "exclude": ["dist", "**/*.spec.ts", "packages"] +} diff --git a/packages/store/yarn.lock b/packages/store/yarn.lock new file mode 100644 index 0000000000..59f8b2244e --- /dev/null +++ b/packages/store/yarn.lock @@ -0,0 +1,3309 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/code-frame@^7.0.0": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" + integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== + dependencies: + "@babel/highlight" "^7.18.6" + +"@babel/helper-validator-identifier@^7.18.6": + version "7.19.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" + integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== + +"@babel/highlight@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" + integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== + dependencies: + "@babel/helper-validator-identifier" "^7.18.6" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@cspotcode/source-map-consumer@0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz#33bf4b7b39c178821606f669bbc447a6a629786b" + integrity sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg== + +"@cspotcode/source-map-support@0.7.0": + version "0.7.0" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz#4789840aa859e46d2f3173727ab707c66bf344f5" + integrity sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA== + dependencies: + "@cspotcode/source-map-consumer" "0.8.0" + +"@ethersproject/abi@^5.1.2": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.7.0.tgz#b3f3e045bbbeed1af3947335c247ad625a44e449" + integrity sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA== + dependencies: + "@ethersproject/address" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/hash" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + +"@ethersproject/abstract-provider@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz#b0a8550f88b6bf9d51f90e4795d48294630cb9ef" + integrity sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/networks" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + "@ethersproject/web" "^5.7.0" + +"@ethersproject/abstract-signer@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz#13f4f32117868452191a4649723cb086d2b596b2" + integrity sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ== + dependencies: + "@ethersproject/abstract-provider" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + +"@ethersproject/address@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.7.0.tgz#19b56c4d74a3b0a46bfdbb6cfcc0a153fc697f37" + integrity sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/rlp" "^5.7.0" + +"@ethersproject/base64@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.7.0.tgz#ac4ee92aa36c1628173e221d0d01f53692059e1c" + integrity sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ== + dependencies: + "@ethersproject/bytes" "^5.7.0" + +"@ethersproject/bignumber@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.7.0.tgz#e2f03837f268ba655ffba03a57853e18a18dc9c2" + integrity sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + bn.js "^5.2.1" + +"@ethersproject/bytes@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.7.0.tgz#a00f6ea8d7e7534d6d87f47188af1148d71f155d" + integrity sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A== + dependencies: + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/constants@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.7.0.tgz#df80a9705a7e08984161f09014ea012d1c75295e" + integrity sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + +"@ethersproject/hash@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.7.0.tgz#eb7aca84a588508369562e16e514b539ba5240a7" + integrity sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g== + dependencies: + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/address" "^5.7.0" + "@ethersproject/base64" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + +"@ethersproject/keccak256@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.7.0.tgz#3186350c6e1cd6aba7940384ec7d6d9db01f335a" + integrity sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg== + dependencies: + "@ethersproject/bytes" "^5.7.0" + js-sha3 "0.8.0" + +"@ethersproject/logger@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.7.0.tgz#6ce9ae168e74fecf287be17062b590852c311892" + integrity sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig== + +"@ethersproject/networks@^5.7.0": + version "5.7.1" + resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.7.1.tgz#118e1a981d757d45ccea6bb58d9fd3d9db14ead6" + integrity sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ== + dependencies: + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/properties@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.7.0.tgz#a6e12cb0439b878aaf470f1902a176033067ed30" + integrity sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw== + dependencies: + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/rlp@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.7.0.tgz#de39e4d5918b9d74d46de93af80b7685a9c21304" + integrity sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/signing-key@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.7.0.tgz#06b2df39411b00bc57c7c09b01d1e41cf1b16ab3" + integrity sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + bn.js "^5.2.1" + elliptic "6.5.4" + hash.js "1.1.7" + +"@ethersproject/strings@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.7.0.tgz#54c9d2a7c57ae8f1205c88a9d3a56471e14d5ed2" + integrity sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/transactions@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.7.0.tgz#91318fc24063e057885a6af13fdb703e1f993d3b" + integrity sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ== + dependencies: + "@ethersproject/address" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/rlp" "^5.7.0" + "@ethersproject/signing-key" "^5.7.0" + +"@ethersproject/web@^5.7.0": + version "5.7.1" + resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.7.1.tgz#de1f285b373149bee5928f4eb7bcb87ee5fbb4ae" + integrity sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w== + dependencies: + "@ethersproject/base64" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + +"@metamask/eth-sig-util@^4.0.0": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@metamask/eth-sig-util/-/eth-sig-util-4.0.1.tgz#3ad61f6ea9ad73ba5b19db780d40d9aae5157088" + integrity sha512-tghyZKLHZjcdlDqCA3gNZmLeR0XvOE9U1qoQO9ohyAZT6Pya+H9vkBPcsyXytmYLNgVoin7CKCmweo/R43V+tQ== + dependencies: + ethereumjs-abi "^0.6.8" + ethereumjs-util "^6.2.1" + ethjs-util "^0.1.6" + tweetnacl "^1.0.3" + tweetnacl-util "^0.15.1" + +"@morgan-stanley/ts-mocking-bird@^0.6.2": + version "0.6.4" + resolved "https://registry.yarnpkg.com/@morgan-stanley/ts-mocking-bird/-/ts-mocking-bird-0.6.4.tgz#2e4b60d42957bab3b50b67dbf14c3da2f62a39f7" + integrity sha512-57VJIflP8eR2xXa9cD1LUawh+Gh+BVQfVu0n6GALyg/AqV/Nz25kDRvws3i9kIe1PTrbsZZOYpsYp6bXPd6nVA== + dependencies: + lodash "^4.17.16" + uuid "^7.0.3" + +"@noble/hashes@1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.1.2.tgz#e9e035b9b166ca0af657a7848eb2718f0f22f183" + integrity sha512-KYRCASVTv6aeUi1tsF8/vpyR7zpfs3FUzy2Jqm+MU+LmUKhQ0y2FpfwqkCcxSg2ua4GALJd8k2R76WxwZGbQpA== + +"@noble/hashes@~1.1.1": + version "1.1.5" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.1.5.tgz#1a0377f3b9020efe2fae03290bd2a12140c95c11" + integrity sha512-LTMZiiLc+V4v1Yi16TD6aX2gmtKszNye0pQgbaLqkvhIqP7nVsSaJsWloGQjJfJ8offaoP5GtX3yY5swbcJxxQ== + +"@noble/secp256k1@1.6.3", "@noble/secp256k1@~1.6.0": + version "1.6.3" + resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.6.3.tgz#7eed12d9f4404b416999d0c87686836c4c5c9b94" + integrity sha512-T04e4iTurVy7I8Sw4+c5OSN9/RkPlo1uKxAomtxQNLq8j1uPAqnsqG1bqvY3Jv7c13gyr6dui0zmh/I3+f/JaQ== + +"@nomicfoundation/ethereumjs-block@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-block/-/ethereumjs-block-4.0.0.tgz#fdd5c045e7baa5169abeed0e1202bf94e4481c49" + integrity sha512-bk8uP8VuexLgyIZAHExH1QEovqx0Lzhc9Ntm63nCRKLHXIZkobaFaeCVwTESV7YkPKUk7NiK11s8ryed4CS9yA== + dependencies: + "@nomicfoundation/ethereumjs-common" "^3.0.0" + "@nomicfoundation/ethereumjs-rlp" "^4.0.0" + "@nomicfoundation/ethereumjs-trie" "^5.0.0" + "@nomicfoundation/ethereumjs-tx" "^4.0.0" + "@nomicfoundation/ethereumjs-util" "^8.0.0" + ethereum-cryptography "0.1.3" + +"@nomicfoundation/ethereumjs-blockchain@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-blockchain/-/ethereumjs-blockchain-6.0.0.tgz#1a8c243a46d4d3691631f139bfb3a4a157187b0c" + integrity sha512-pLFEoea6MWd81QQYSReLlLfH7N9v7lH66JC/NMPN848ySPPQA5renWnE7wPByfQFzNrPBuDDRFFULMDmj1C0xw== + dependencies: + "@nomicfoundation/ethereumjs-block" "^4.0.0" + "@nomicfoundation/ethereumjs-common" "^3.0.0" + "@nomicfoundation/ethereumjs-ethash" "^2.0.0" + "@nomicfoundation/ethereumjs-rlp" "^4.0.0" + "@nomicfoundation/ethereumjs-trie" "^5.0.0" + "@nomicfoundation/ethereumjs-util" "^8.0.0" + abstract-level "^1.0.3" + debug "^4.3.3" + ethereum-cryptography "0.1.3" + level "^8.0.0" + lru-cache "^5.1.1" + memory-level "^1.0.0" + +"@nomicfoundation/ethereumjs-common@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-common/-/ethereumjs-common-3.0.0.tgz#f6bcc7753994555e49ab3aa517fc8bcf89c280b9" + integrity sha512-WS7qSshQfxoZOpHG/XqlHEGRG1zmyjYrvmATvc4c62+gZXgre1ymYP8ZNgx/3FyZY0TWe9OjFlKOfLqmgOeYwA== + dependencies: + "@nomicfoundation/ethereumjs-util" "^8.0.0" + crc-32 "^1.2.0" + +"@nomicfoundation/ethereumjs-ethash@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-ethash/-/ethereumjs-ethash-2.0.0.tgz#11539c32fe0990e1122ff987d1b84cfa34774e81" + integrity sha512-WpDvnRncfDUuXdsAXlI4lXbqUDOA+adYRQaEezIkxqDkc+LDyYDbd/xairmY98GnQzo1zIqsIL6GB5MoMSJDew== + dependencies: + "@nomicfoundation/ethereumjs-block" "^4.0.0" + "@nomicfoundation/ethereumjs-rlp" "^4.0.0" + "@nomicfoundation/ethereumjs-util" "^8.0.0" + abstract-level "^1.0.3" + bigint-crypto-utils "^3.0.23" + ethereum-cryptography "0.1.3" + +"@nomicfoundation/ethereumjs-evm@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-evm/-/ethereumjs-evm-1.0.0.tgz#99cd173c03b59107c156a69c5e215409098a370b" + integrity sha512-hVS6qRo3V1PLKCO210UfcEQHvlG7GqR8iFzp0yyjTg2TmJQizcChKgWo8KFsdMw6AyoLgLhHGHw4HdlP8a4i+Q== + dependencies: + "@nomicfoundation/ethereumjs-common" "^3.0.0" + "@nomicfoundation/ethereumjs-util" "^8.0.0" + "@types/async-eventemitter" "^0.2.1" + async-eventemitter "^0.2.4" + debug "^4.3.3" + ethereum-cryptography "0.1.3" + mcl-wasm "^0.7.1" + rustbn.js "~0.2.0" + +"@nomicfoundation/ethereumjs-rlp@^4.0.0", "@nomicfoundation/ethereumjs-rlp@^4.0.0-beta.2": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-rlp/-/ethereumjs-rlp-4.0.0.tgz#d9a9c5f0f10310c8849b6525101de455a53e771d" + integrity sha512-GaSOGk5QbUk4eBP5qFbpXoZoZUj/NrW7MRa0tKY4Ew4c2HAS0GXArEMAamtFrkazp0BO4K5p2ZCG3b2FmbShmw== + +"@nomicfoundation/ethereumjs-statemanager@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-statemanager/-/ethereumjs-statemanager-1.0.0.tgz#14a9d4e1c828230368f7ab520c144c34d8721e4b" + integrity sha512-jCtqFjcd2QejtuAMjQzbil/4NHf5aAWxUc+CvS0JclQpl+7M0bxMofR2AJdtz+P3u0ke2euhYREDiE7iSO31vQ== + dependencies: + "@nomicfoundation/ethereumjs-common" "^3.0.0" + "@nomicfoundation/ethereumjs-rlp" "^4.0.0" + "@nomicfoundation/ethereumjs-trie" "^5.0.0" + "@nomicfoundation/ethereumjs-util" "^8.0.0" + debug "^4.3.3" + ethereum-cryptography "0.1.3" + functional-red-black-tree "^1.0.1" + +"@nomicfoundation/ethereumjs-trie@^5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-trie/-/ethereumjs-trie-5.0.0.tgz#dcfbe3be53a94bc061c9767a396c16702bc2f5b7" + integrity sha512-LIj5XdE+s+t6WSuq/ttegJzZ1vliwg6wlb+Y9f4RlBpuK35B9K02bO7xU+E6Rgg9RGptkWd6TVLdedTI4eNc2A== + dependencies: + "@nomicfoundation/ethereumjs-rlp" "^4.0.0" + "@nomicfoundation/ethereumjs-util" "^8.0.0" + ethereum-cryptography "0.1.3" + readable-stream "^3.6.0" + +"@nomicfoundation/ethereumjs-tx@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-tx/-/ethereumjs-tx-4.0.0.tgz#59dc7452b0862b30342966f7052ab9a1f7802f52" + integrity sha512-Gg3Lir2lNUck43Kp/3x6TfBNwcWC9Z1wYue9Nz3v4xjdcv6oDW9QSMJxqsKw9QEGoBBZ+gqwpW7+F05/rs/g1w== + dependencies: + "@nomicfoundation/ethereumjs-common" "^3.0.0" + "@nomicfoundation/ethereumjs-rlp" "^4.0.0" + "@nomicfoundation/ethereumjs-util" "^8.0.0" + ethereum-cryptography "0.1.3" + +"@nomicfoundation/ethereumjs-util@^8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-util/-/ethereumjs-util-8.0.0.tgz#deb2b15d2c308a731e82977aefc4e61ca0ece6c5" + integrity sha512-2emi0NJ/HmTG+CGY58fa+DQuAoroFeSH9gKu9O6JnwTtlzJtgfTixuoOqLEgyyzZVvwfIpRueuePb8TonL1y+A== + dependencies: + "@nomicfoundation/ethereumjs-rlp" "^4.0.0-beta.2" + ethereum-cryptography "0.1.3" + +"@nomicfoundation/ethereumjs-vm@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-vm/-/ethereumjs-vm-6.0.0.tgz#2bb50d332bf41790b01a3767ffec3987585d1de6" + integrity sha512-JMPxvPQ3fzD063Sg3Tp+UdwUkVxMoo1uML6KSzFhMH3hoQi/LMuXBoEHAoW83/vyNS9BxEe6jm6LmT5xdeEJ6w== + dependencies: + "@nomicfoundation/ethereumjs-block" "^4.0.0" + "@nomicfoundation/ethereumjs-blockchain" "^6.0.0" + "@nomicfoundation/ethereumjs-common" "^3.0.0" + "@nomicfoundation/ethereumjs-evm" "^1.0.0" + "@nomicfoundation/ethereumjs-rlp" "^4.0.0" + "@nomicfoundation/ethereumjs-statemanager" "^1.0.0" + "@nomicfoundation/ethereumjs-trie" "^5.0.0" + "@nomicfoundation/ethereumjs-tx" "^4.0.0" + "@nomicfoundation/ethereumjs-util" "^8.0.0" + "@types/async-eventemitter" "^0.2.1" + async-eventemitter "^0.2.4" + debug "^4.3.3" + ethereum-cryptography "0.1.3" + functional-red-black-tree "^1.0.1" + mcl-wasm "^0.7.1" + rustbn.js "~0.2.0" + +"@nomicfoundation/solidity-analyzer-darwin-arm64@0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-darwin-arm64/-/solidity-analyzer-darwin-arm64-0.1.0.tgz#83a7367342bd053a76d04bbcf4f373fef07cf760" + integrity sha512-vEF3yKuuzfMHsZecHQcnkUrqm8mnTWfJeEVFHpg+cO+le96xQA4lAJYdUan8pXZohQxv1fSReQsn4QGNuBNuCw== + +"@nomicfoundation/solidity-analyzer-darwin-x64@0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-darwin-x64/-/solidity-analyzer-darwin-x64-0.1.0.tgz#1225f7da647ae1ad25a87125664704ecc0af6ccc" + integrity sha512-dlHeIg0pTL4dB1l9JDwbi/JG6dHQaU1xpDK+ugYO8eJ1kxx9Dh2isEUtA4d02cQAl22cjOHTvifAk96A+ItEHA== + +"@nomicfoundation/solidity-analyzer-freebsd-x64@0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-freebsd-x64/-/solidity-analyzer-freebsd-x64-0.1.0.tgz#dbc052dcdfd50ae50fd5ae1788b69b4e0fa40040" + integrity sha512-WFCZYMv86WowDA4GiJKnebMQRt3kCcFqHeIomW6NMyqiKqhK1kIZCxSLDYsxqlx396kKLPN1713Q1S8tu68GKg== + +"@nomicfoundation/solidity-analyzer-linux-arm64-gnu@0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-linux-arm64-gnu/-/solidity-analyzer-linux-arm64-gnu-0.1.0.tgz#e6b2eea633995b557e74e881d2a43eab4760903d" + integrity sha512-DTw6MNQWWlCgc71Pq7CEhEqkb7fZnS7oly13pujs4cMH1sR0JzNk90Mp1zpSCsCs4oKan2ClhMlLKtNat/XRKQ== + +"@nomicfoundation/solidity-analyzer-linux-arm64-musl@0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-linux-arm64-musl/-/solidity-analyzer-linux-arm64-musl-0.1.0.tgz#af81107f5afa794f19988a368647727806e18dc4" + integrity sha512-wUpUnR/3GV5Da88MhrxXh/lhb9kxh9V3Jya2NpBEhKDIRCDmtXMSqPMXHZmOR9DfCwCvG6vLFPr/+YrPCnUN0w== + +"@nomicfoundation/solidity-analyzer-linux-x64-gnu@0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-linux-x64-gnu/-/solidity-analyzer-linux-x64-gnu-0.1.0.tgz#6877e1da1a06a9f08446070ab6e0a5347109f868" + integrity sha512-lR0AxK1x/MeKQ/3Pt923kPvwigmGX3OxeU5qNtQ9pj9iucgk4PzhbS3ruUeSpYhUxG50jN4RkIGwUMoev5lguw== + +"@nomicfoundation/solidity-analyzer-linux-x64-musl@0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-linux-x64-musl/-/solidity-analyzer-linux-x64-musl-0.1.0.tgz#bb6cd83a0c259eccef4183796b6329a66cf7ebd9" + integrity sha512-A1he/8gy/JeBD3FKvmI6WUJrGrI5uWJNr5Xb9WdV+DK0F8msuOqpEByLlnTdLkXMwW7nSl3awvLezOs9xBHJEg== + +"@nomicfoundation/solidity-analyzer-win32-arm64-msvc@0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-win32-arm64-msvc/-/solidity-analyzer-win32-arm64-msvc-0.1.0.tgz#9d4bca1cc9a1333fde985675083b0b7d165f6076" + integrity sha512-7x5SXZ9R9H4SluJZZP8XPN+ju7Mx+XeUMWZw7ZAqkdhP5mK19I4vz3x0zIWygmfE8RT7uQ5xMap0/9NPsO+ykw== + +"@nomicfoundation/solidity-analyzer-win32-ia32-msvc@0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-win32-ia32-msvc/-/solidity-analyzer-win32-ia32-msvc-0.1.0.tgz#0db5bfc6aa952bea4098d8d2c8947b4e5c4337ee" + integrity sha512-m7w3xf+hnE774YRXu+2mGV7RiF3QJtUoiYU61FascCkQhX3QMQavh7saH/vzb2jN5D24nT/jwvaHYX/MAM9zUw== + +"@nomicfoundation/solidity-analyzer-win32-x64-msvc@0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-win32-x64-msvc/-/solidity-analyzer-win32-x64-msvc-0.1.0.tgz#2e0f39a2924dcd77db6b419828595e984fabcb33" + integrity sha512-xCuybjY0sLJQnJhupiFAXaek2EqF0AP0eBjgzaalPXSNvCEN6ZYHvUzdA50ENDVeSYFXcUsYf3+FsD3XKaeptA== + +"@nomicfoundation/solidity-analyzer@^0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer/-/solidity-analyzer-0.1.0.tgz#e5ddc43ad5c0aab96e5054520d8e16212e125f50" + integrity sha512-xGWAiVCGOycvGiP/qrlf9f9eOn7fpNbyJygcB0P21a1MDuVPlKt0Srp7rvtBEutYQ48ouYnRXm33zlRnlTOPHg== + optionalDependencies: + "@nomicfoundation/solidity-analyzer-darwin-arm64" "0.1.0" + "@nomicfoundation/solidity-analyzer-darwin-x64" "0.1.0" + "@nomicfoundation/solidity-analyzer-freebsd-x64" "0.1.0" + "@nomicfoundation/solidity-analyzer-linux-arm64-gnu" "0.1.0" + "@nomicfoundation/solidity-analyzer-linux-arm64-musl" "0.1.0" + "@nomicfoundation/solidity-analyzer-linux-x64-gnu" "0.1.0" + "@nomicfoundation/solidity-analyzer-linux-x64-musl" "0.1.0" + "@nomicfoundation/solidity-analyzer-win32-arm64-msvc" "0.1.0" + "@nomicfoundation/solidity-analyzer-win32-ia32-msvc" "0.1.0" + "@nomicfoundation/solidity-analyzer-win32-x64-msvc" "0.1.0" + +"@scure/base@~1.1.0": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.1.tgz#ebb651ee52ff84f420097055f4bf46cfba403938" + integrity sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA== + +"@scure/bip32@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.1.0.tgz#dea45875e7fbc720c2b4560325f1cf5d2246d95b" + integrity sha512-ftTW3kKX54YXLCxH6BB7oEEoJfoE2pIgw7MINKAs5PsS6nqKPuKk1haTF/EuHmYqG330t5GSrdmtRuHaY1a62Q== + dependencies: + "@noble/hashes" "~1.1.1" + "@noble/secp256k1" "~1.6.0" + "@scure/base" "~1.1.0" + +"@scure/bip39@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.1.0.tgz#92f11d095bae025f166bef3defcc5bf4945d419a" + integrity sha512-pwrPOS16VeTKg98dYXQyIjJEcWfz7/1YJIwxUEPFfQPtc86Ym/1sVgQ2RLoD43AazMk2l/unK4ITySSpW2+82w== + dependencies: + "@noble/hashes" "~1.1.1" + "@scure/base" "~1.1.0" + +"@sentry/core@5.30.0": + version "5.30.0" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-5.30.0.tgz#6b203664f69e75106ee8b5a2fe1d717379b331f3" + integrity sha512-TmfrII8w1PQZSZgPpUESqjB+jC6MvZJZdLtE/0hZ+SrnKhW3x5WlYLvTXZpcWePYBku7rl2wn1RZu6uT0qCTeg== + dependencies: + "@sentry/hub" "5.30.0" + "@sentry/minimal" "5.30.0" + "@sentry/types" "5.30.0" + "@sentry/utils" "5.30.0" + tslib "^1.9.3" + +"@sentry/hub@5.30.0": + version "5.30.0" + resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-5.30.0.tgz#2453be9b9cb903404366e198bd30c7ca74cdc100" + integrity sha512-2tYrGnzb1gKz2EkMDQcfLrDTvmGcQPuWxLnJKXJvYTQDGLlEvi2tWz1VIHjunmOvJrB5aIQLhm+dcMRwFZDCqQ== + dependencies: + "@sentry/types" "5.30.0" + "@sentry/utils" "5.30.0" + tslib "^1.9.3" + +"@sentry/minimal@5.30.0": + version "5.30.0" + resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-5.30.0.tgz#ce3d3a6a273428e0084adcb800bc12e72d34637b" + integrity sha512-BwWb/owZKtkDX+Sc4zCSTNcvZUq7YcH3uAVlmh/gtR9rmUvbzAA3ewLuB3myi4wWRAMEtny6+J/FN/x+2wn9Xw== + dependencies: + "@sentry/hub" "5.30.0" + "@sentry/types" "5.30.0" + tslib "^1.9.3" + +"@sentry/node@^5.18.1": + version "5.30.0" + resolved "https://registry.yarnpkg.com/@sentry/node/-/node-5.30.0.tgz#4ca479e799b1021285d7fe12ac0858951c11cd48" + integrity sha512-Br5oyVBF0fZo6ZS9bxbJZG4ApAjRqAnqFFurMVJJdunNb80brh7a5Qva2kjhm+U6r9NJAB5OmDyPkA1Qnt+QVg== + dependencies: + "@sentry/core" "5.30.0" + "@sentry/hub" "5.30.0" + "@sentry/tracing" "5.30.0" + "@sentry/types" "5.30.0" + "@sentry/utils" "5.30.0" + cookie "^0.4.1" + https-proxy-agent "^5.0.0" + lru_map "^0.3.3" + tslib "^1.9.3" + +"@sentry/tracing@5.30.0": + version "5.30.0" + resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-5.30.0.tgz#501d21f00c3f3be7f7635d8710da70d9419d4e1f" + integrity sha512-dUFowCr0AIMwiLD7Fs314Mdzcug+gBVo/+NCMyDw8tFxJkwWAKl7Qa2OZxLQ0ZHjakcj1hNKfCQJ9rhyfOl4Aw== + dependencies: + "@sentry/hub" "5.30.0" + "@sentry/minimal" "5.30.0" + "@sentry/types" "5.30.0" + "@sentry/utils" "5.30.0" + tslib "^1.9.3" + +"@sentry/types@5.30.0": + version "5.30.0" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-5.30.0.tgz#19709bbe12a1a0115bc790b8942917da5636f402" + integrity sha512-R8xOqlSTZ+htqrfteCWU5Nk0CDN5ApUTvrlvBuiH1DyP6czDZ4ktbZB0hAgBlVcK0U+qpD3ag3Tqqpa5Q67rPw== + +"@sentry/utils@5.30.0": + version "5.30.0" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-5.30.0.tgz#9a5bd7ccff85ccfe7856d493bffa64cabc41e980" + integrity sha512-zaYmoH0NWWtvnJjC9/CBseXMtKHm/tm40sz3YfJRxeQjyzRqNQPgivpd9R/oDJCYj999mzdW382p/qi2ypjLww== + dependencies: + "@sentry/types" "5.30.0" + tslib "^1.9.3" + +"@solidity-parser/parser@^0.14.5": + version "0.14.5" + resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.14.5.tgz#87bc3cc7b068e08195c219c91cd8ddff5ef1a804" + integrity sha512-6dKnHZn7fg/iQATVEzqyUOyEidbn05q7YA2mQ9hC0MMXhhV3/JrsxmFSYZAcr7j1yUP700LLhTruvJ3MiQmjJg== + dependencies: + antlr4ts "^0.5.0-alpha.4" + +"@tsconfig/node10@^1.0.7": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" + integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA== + +"@tsconfig/node12@^1.0.7": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" + integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== + +"@tsconfig/node14@^1.0.0": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" + integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== + +"@tsconfig/node16@^1.0.2": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e" + integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== + +"@typechain/ethers-v5@^9.0.0": + version "9.0.0" + resolved "https://registry.yarnpkg.com/@typechain/ethers-v5/-/ethers-v5-9.0.0.tgz#6aa93bea7425c0463bd8a61eea3643540ef851bd" + integrity sha512-bAanuPl1L2itaUdMvor/QvwnIH+TM/CmG00q17Ilv3ZZMeJ2j8HcarhgJUZ9pBY1teBb85P8cC03dz3mSSx+tQ== + dependencies: + lodash "^4.17.15" + ts-essentials "^7.0.1" + +"@types/async-eventemitter@^0.2.1": + version "0.2.1" + resolved "https://registry.yarnpkg.com/@types/async-eventemitter/-/async-eventemitter-0.2.1.tgz#f8e6280e87e8c60b2b938624b0a3530fb3e24712" + integrity sha512-M2P4Ng26QbAeITiH7w1d7OxtldgfAe0wobpyJzVK/XOb0cUGKU2R4pfAhqcJBXAe2ife5ZOhSv4wk7p+ffURtg== + +"@types/bn.js@^4.11.3": + version "4.11.6" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c" + integrity sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg== + dependencies: + "@types/node" "*" + +"@types/bn.js@^5.1.0": + version "5.1.1" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.1.tgz#b51e1b55920a4ca26e9285ff79936bbdec910682" + integrity sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g== + dependencies: + "@types/node" "*" + +"@types/lru-cache@^5.1.0": + version "5.1.1" + resolved "https://registry.yarnpkg.com/@types/lru-cache/-/lru-cache-5.1.1.tgz#c48c2e27b65d2a153b19bfc1a317e30872e01eef" + integrity sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw== + +"@types/mocha@^9.1.1": + version "9.1.1" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-9.1.1.tgz#e7c4f1001eefa4b8afbd1eee27a237fee3bf29c4" + integrity sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw== + +"@types/node@*": + version "18.11.18" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.18.tgz#8dfb97f0da23c2293e554c5a50d61ef134d7697f" + integrity sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA== + +"@types/pbkdf2@^3.0.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@types/pbkdf2/-/pbkdf2-3.1.0.tgz#039a0e9b67da0cdc4ee5dab865caa6b267bb66b1" + integrity sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ== + dependencies: + "@types/node" "*" + +"@types/prettier@^2.1.1": + version "2.7.2" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.2.tgz#6c2324641cc4ba050a8c710b2b251b377581fbf0" + integrity sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg== + +"@types/secp256k1@^4.0.1": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@types/secp256k1/-/secp256k1-4.0.3.tgz#1b8e55d8e00f08ee7220b4d59a6abe89c37a901c" + integrity sha512-Da66lEIFeIz9ltsdMZcpQvmrmmoqrfju8pm1BH8WbYjZSwUgCwXLb9C+9XYogwBITnbsSaMdVPb2ekf7TV+03w== + dependencies: + "@types/node" "*" + +abort-controller@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== + dependencies: + event-target-shim "^5.0.0" + +abstract-level@^1.0.0, abstract-level@^1.0.2, abstract-level@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/abstract-level/-/abstract-level-1.0.3.tgz#78a67d3d84da55ee15201486ab44c09560070741" + integrity sha512-t6jv+xHy+VYwc4xqZMn2Pa9DjcdzvzZmQGRjTFc8spIbRGHgBrEKbPq+rYXc7CCo0lxgYvSgKVg9qZAhpVQSjA== + dependencies: + buffer "^6.0.3" + catering "^2.1.0" + is-buffer "^2.0.5" + level-supports "^4.0.0" + level-transcoder "^1.0.1" + module-error "^1.0.1" + queue-microtask "^1.2.3" + +acorn-jsx@^5.0.0: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn-walk@^8.1.1: + version "8.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" + integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== + +acorn@^6.0.7: + version "6.4.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" + integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== + +acorn@^8.4.1: + version "8.8.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" + integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== + +adm-zip@^0.4.16: + version "0.4.16" + resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.4.16.tgz#cf4c508fdffab02c269cbc7f471a875f05570365" + integrity sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg== + +agent-base@6: + version "6.0.2" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" + +aggregate-error@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== + dependencies: + clean-stack "^2.0.0" + indent-string "^4.0.0" + +ajv@^6.10.2, ajv@^6.6.1, ajv@^6.9.1: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-colors@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" + integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== + +ansi-colors@^4.1.1: + version "4.1.3" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" + integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== + +ansi-escapes@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" + integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== + +ansi-escapes@^4.3.0: + version "4.3.2" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== + dependencies: + type-fest "^0.21.3" + +ansi-regex@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.1.tgz#123d6479e92ad45ad897d4054e3c7ca7db4944e1" + integrity sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw== + +ansi-regex@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.1.tgz#164daac87ab2d6f6db3a29875e2d1766582dabed" + integrity sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g== + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^3.2.0, ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +antlr4@4.7.1: + version "4.7.1" + resolved "https://registry.yarnpkg.com/antlr4/-/antlr4-4.7.1.tgz#69984014f096e9e775f53dd9744bf994d8959773" + integrity sha512-haHyTW7Y9joE5MVs37P2lNYfU2RWBLfcRDD8OWldcdZm5TiCE91B5Xl1oWSwiDUSd4rlExpt2pu1fksYQjRBYQ== + +antlr4ts@^0.5.0-alpha.4: + version "0.5.0-alpha.4" + resolved "https://registry.yarnpkg.com/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz#71702865a87478ed0b40c0709f422cf14d51652a" + integrity sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ== + +anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +array-back@^3.0.1, array-back@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/array-back/-/array-back-3.1.0.tgz#b8859d7a508871c9a7b2cf42f99428f65e96bfb0" + integrity sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q== + +array-back@^4.0.1, array-back@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/array-back/-/array-back-4.0.2.tgz#8004e999a6274586beeb27342168652fdb89fa1e" + integrity sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg== + +ast-parents@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/ast-parents/-/ast-parents-0.0.1.tgz#508fd0f05d0c48775d9eccda2e174423261e8dd3" + integrity sha512-XHusKxKz3zoYk1ic8Un640joHbFMhbqneyoZfoKnEGtf2ey9Uh/IdpcQplODdO/kENaMIWsD0nJm4+wX3UNLHA== + +astral-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" + integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== + +async-eventemitter@^0.2.4: + version "0.2.4" + resolved "https://registry.yarnpkg.com/async-eventemitter/-/async-eventemitter-0.2.4.tgz#f5e7c8ca7d3e46aab9ec40a292baf686a0bafaca" + integrity sha512-pd20BwL7Yt1zwDFy+8MX8F1+WCT8aQeKj0kQnTrH9WaeRETlRamVhD0JtRPmrV4GfOJ2F9CvdQkZeZhnh2TuHw== + dependencies: + async "^2.4.0" + +async@^2.4.0: + version "2.6.4" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221" + integrity sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA== + dependencies: + lodash "^4.17.14" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +base-x@^3.0.2: + version "3.0.9" + resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.9.tgz#6349aaabb58526332de9f60995e548a53fe21320" + integrity sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ== + dependencies: + safe-buffer "^5.0.1" + +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +bigint-crypto-utils@^3.0.23: + version "3.1.8" + resolved "https://registry.yarnpkg.com/bigint-crypto-utils/-/bigint-crypto-utils-3.1.8.tgz#e2e0f40cf45488f9d7f0e32ff84152aa73819d5d" + integrity sha512-+VMV9Laq8pXLBKKKK49nOoq9bfR3j7NNQAtbA617a4nw9bVLo8rsqkKMBgM2AJWlNX9fEIyYaYX+d0laqYV4tw== + dependencies: + bigint-mod-arith "^3.1.0" + +bigint-mod-arith@^3.1.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bigint-mod-arith/-/bigint-mod-arith-3.1.2.tgz#658e416bc593a463d97b59766226d0a3021a76b1" + integrity sha512-nx8J8bBeiRR+NlsROFH9jHswW5HO8mgfOSqW0AmjicMMvaONDa8AO+5ViKDUUNytBPWiwfvZP4/Bj4Y3lUfvgQ== + +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + +blakejs@^1.1.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.2.1.tgz#5057e4206eadb4a97f7c0b6e197a505042fc3814" + integrity sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ== + +bn.js@^4.11.0, bn.js@^4.11.8, bn.js@^4.11.9: + version "4.12.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" + integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== + +bn.js@^5.2.0, bn.js@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" + integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + +braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +brorand@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w== + +browser-level@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/browser-level/-/browser-level-1.0.1.tgz#36e8c3183d0fe1c405239792faaab5f315871011" + integrity sha512-XECYKJ+Dbzw0lbydyQuJzwNXtOpbMSq737qxJN11sIRTErOMShvDpbzTlgju7orJKvx4epULolZAuJGLzCmWRQ== + dependencies: + abstract-level "^1.0.2" + catering "^2.1.1" + module-error "^1.0.2" + run-parallel-limit "^1.1.0" + +browser-stdout@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== + +browserify-aes@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" + integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== + dependencies: + buffer-xor "^1.0.3" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.3" + inherits "^2.0.1" + safe-buffer "^5.0.1" + +bs58@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" + integrity sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw== + dependencies: + base-x "^3.0.2" + +bs58check@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/bs58check/-/bs58check-2.1.2.tgz#53b018291228d82a5aa08e7d796fdafda54aebfc" + integrity sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA== + dependencies: + bs58 "^4.0.0" + create-hash "^1.1.0" + safe-buffer "^5.1.2" + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +buffer-xor@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + integrity sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ== + +buffer@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + +busboy@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893" + integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA== + dependencies: + streamsearch "^1.1.0" + +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + +call-bind@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" + integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== + dependencies: + function-bind "^1.1.1" + get-intrinsic "^1.0.2" + +caller-callsite@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" + integrity sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ== + dependencies: + callsites "^2.0.0" + +caller-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4" + integrity sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A== + dependencies: + caller-callsite "^2.0.0" + +callsites@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" + integrity sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ== + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camelcase@^6.0.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + +catering@^2.1.0, catering@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/catering/-/catering-2.1.1.tgz#66acba06ed5ee28d5286133982a927de9a04b510" + integrity sha512-K7Qy8O9p76sL3/3m7/zLKbRkyOlSZAgzEaLhyj2mXS8PsCud2Eo4hAb8aLtZqHh0QGqLcb9dlJSu6lHRVENm1w== + +chalk@^2.0.0, chalk@^2.1.0, chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^4.1.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== + +chokidar@3.5.3, chokidar@^3.4.0: + version "3.5.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +ci-info@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== + +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +classic-level@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/classic-level/-/classic-level-1.2.0.tgz#2d52bdec8e7a27f534e67fdeb890abef3e643c27" + integrity sha512-qw5B31ANxSluWz9xBzklRWTUAJ1SXIdaVKTVS7HcTGKOAmExx65Wo5BUICW+YGORe2FOUaDghoI9ZDxj82QcFg== + dependencies: + abstract-level "^1.0.2" + catering "^2.1.0" + module-error "^1.0.1" + napi-macros "~2.0.0" + node-gyp-build "^4.3.0" + +clean-stack@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== + +cli-cursor@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" + integrity sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw== + dependencies: + restore-cursor "^2.0.0" + +cli-width@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48" + integrity sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw== + +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +command-exists@^1.2.8: + version "1.2.9" + resolved "https://registry.yarnpkg.com/command-exists/-/command-exists-1.2.9.tgz#c50725af3808c8ab0260fd60b01fbfa25b954f69" + integrity sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w== + +command-line-args@^5.1.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/command-line-args/-/command-line-args-5.2.1.tgz#c44c32e437a57d7c51157696893c5909e9cec42e" + integrity sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg== + dependencies: + array-back "^3.1.0" + find-replace "^3.0.0" + lodash.camelcase "^4.3.0" + typical "^4.0.0" + +command-line-usage@^6.1.0: + version "6.1.3" + resolved "https://registry.yarnpkg.com/command-line-usage/-/command-line-usage-6.1.3.tgz#428fa5acde6a838779dfa30e44686f4b6761d957" + integrity sha512-sH5ZSPr+7UStsloltmDh7Ce5fb8XPlHyoPzTpyyMuYCtervL65+ubVZ6Q61cFtFl62UyJlc8/JwERRbAFPUqgw== + dependencies: + array-back "^4.0.2" + chalk "^2.4.2" + table-layout "^1.0.2" + typical "^5.2.0" + +commander@2.18.0: + version "2.18.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.18.0.tgz#2bf063ddee7c7891176981a2cc798e5754bc6970" + integrity sha512-6CYPa+JP2ftfRU2qkDK+UTVeQYosOg/2GbcjIcKPHfinyOLPVGXu/ovN86RP49Re5ndJK1N0kuiidFFuepc4ZQ== + +commander@3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/commander/-/commander-3.0.2.tgz#6837c3fb677ad9933d1cfba42dd14d5117d6b39e" + integrity sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +cookie@^0.4.1: + version "0.4.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" + integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== + +cosmiconfig@^5.0.7: + version "5.2.1" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" + integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== + dependencies: + import-fresh "^2.0.0" + is-directory "^0.3.1" + js-yaml "^3.13.1" + parse-json "^4.0.0" + +crc-32@^1.2.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.2.tgz#3cad35a934b8bf71f25ca524b6da51fb7eace2ff" + integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ== + +create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + md5.js "^1.3.4" + ripemd160 "^2.0.1" + sha.js "^2.4.0" + +create-hmac@^1.1.4, create-hmac@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + +cross-spawn@^6.0.5: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +debug@4, debug@4.3.4, debug@^4.0.1, debug@^4.1.1, debug@^4.3.1, debug@^4.3.3: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +decamelize@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" + integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== + +deep-extend@~0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + +deep-is@~0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + +depd@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +diff@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" + integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== + +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +"ds-test@https://github.com/dapphub/ds-test.git#c7a36fb236f298e04edf28e2fee385b80f53945f": + version "0.0.0" + resolved "https://github.com/dapphub/ds-test.git#c7a36fb236f298e04edf28e2fee385b80f53945f" + +elliptic@6.5.4, elliptic@^6.5.2, elliptic@^6.5.4: + version "6.5.4" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" + integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +enquirer@^2.3.0: + version "2.3.6" + resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" + integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== + dependencies: + ansi-colors "^4.1.1" + +env-paths@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" + integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== + +error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + +escape-string-regexp@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== + +eslint-scope@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" + integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg== + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + +eslint-utils@^1.3.1: + version "1.4.3" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" + integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q== + dependencies: + eslint-visitor-keys "^1.1.0" + +eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" + integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== + +eslint@^5.6.0: + version "5.16.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.16.0.tgz#a1e3ac1aae4a3fbd8296fcf8f7ab7314cbb6abea" + integrity sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg== + dependencies: + "@babel/code-frame" "^7.0.0" + ajv "^6.9.1" + chalk "^2.1.0" + cross-spawn "^6.0.5" + debug "^4.0.1" + doctrine "^3.0.0" + eslint-scope "^4.0.3" + eslint-utils "^1.3.1" + eslint-visitor-keys "^1.0.0" + espree "^5.0.1" + esquery "^1.0.1" + esutils "^2.0.2" + file-entry-cache "^5.0.1" + functional-red-black-tree "^1.0.1" + glob "^7.1.2" + globals "^11.7.0" + ignore "^4.0.6" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + inquirer "^6.2.2" + js-yaml "^3.13.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.3.0" + lodash "^4.17.11" + minimatch "^3.0.4" + mkdirp "^0.5.1" + natural-compare "^1.4.0" + optionator "^0.8.2" + path-is-inside "^1.0.2" + progress "^2.0.0" + regexpp "^2.0.1" + semver "^5.5.1" + strip-ansi "^4.0.0" + strip-json-comments "^2.0.1" + table "^5.2.3" + text-table "^0.2.0" + +espree@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-5.0.1.tgz#5d6526fa4fc7f0788a5cf75b15f30323e2f81f7a" + integrity sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A== + dependencies: + acorn "^6.0.7" + acorn-jsx "^5.0.0" + eslint-visitor-keys "^1.0.0" + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esquery@^1.0.1: + version "1.4.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" + integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +ethereum-cryptography@0.1.3, ethereum-cryptography@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz#8d6143cfc3d74bf79bbd8edecdf29e4ae20dd191" + integrity sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ== + dependencies: + "@types/pbkdf2" "^3.0.0" + "@types/secp256k1" "^4.0.1" + blakejs "^1.1.0" + browserify-aes "^1.2.0" + bs58check "^2.1.2" + create-hash "^1.2.0" + create-hmac "^1.1.7" + hash.js "^1.1.7" + keccak "^3.0.0" + pbkdf2 "^3.0.17" + randombytes "^2.1.0" + safe-buffer "^5.1.2" + scrypt-js "^3.0.0" + secp256k1 "^4.0.1" + setimmediate "^1.0.5" + +ethereum-cryptography@^1.0.3: + version "1.1.2" + resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-1.1.2.tgz#74f2ac0f0f5fe79f012c889b3b8446a9a6264e6d" + integrity sha512-XDSJlg4BD+hq9N2FjvotwUET9Tfxpxc3kWGE2AqUG5vcbeunnbImVk3cj6e/xT3phdW21mE8R5IugU4fspQDcQ== + dependencies: + "@noble/hashes" "1.1.2" + "@noble/secp256k1" "1.6.3" + "@scure/bip32" "1.1.0" + "@scure/bip39" "1.1.0" + +ethereumjs-abi@^0.6.8: + version "0.6.8" + resolved "https://registry.yarnpkg.com/ethereumjs-abi/-/ethereumjs-abi-0.6.8.tgz#71bc152db099f70e62f108b7cdfca1b362c6fcae" + integrity sha512-Tx0r/iXI6r+lRsdvkFDlut0N08jWMnKRZ6Gkq+Nmw75lZe4e6o3EkSnkaBP5NF6+m5PTGAr9JP43N3LyeoglsA== + dependencies: + bn.js "^4.11.8" + ethereumjs-util "^6.0.0" + +ethereumjs-util@^6.0.0, ethereumjs-util@^6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz#fcb4e4dd5ceacb9d2305426ab1a5cd93e3163b69" + integrity sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw== + dependencies: + "@types/bn.js" "^4.11.3" + bn.js "^4.11.0" + create-hash "^1.1.2" + elliptic "^6.5.2" + ethereum-cryptography "^0.1.3" + ethjs-util "0.1.6" + rlp "^2.2.3" + +ethjs-util@0.1.6, ethjs-util@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/ethjs-util/-/ethjs-util-0.1.6.tgz#f308b62f185f9fe6237132fb2a9818866a5cd536" + integrity sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w== + dependencies: + is-hex-prefixed "1.0.0" + strip-hex-prefix "1.0.0" + +event-target-shim@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== + +evp_bytestokey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== + dependencies: + md5.js "^1.3.4" + safe-buffer "^5.1.1" + +external-editor@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + +fast-deep-equal@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-diff@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" + integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +figures@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" + integrity sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA== + dependencies: + escape-string-regexp "^1.0.5" + +file-entry-cache@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" + integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== + dependencies: + flat-cache "^2.0.1" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +find-replace@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-replace/-/find-replace-3.0.0.tgz#3e7e23d3b05167a76f770c9fbd5258b0def68c38" + integrity sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ== + dependencies: + array-back "^3.0.1" + +find-up@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +find-up@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + integrity sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ== + dependencies: + locate-path "^2.0.0" + +flat-cache@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" + integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== + dependencies: + flatted "^2.0.0" + rimraf "2.6.3" + write "1.0.3" + +flat@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" + integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== + +flatted@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" + integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== + +follow-redirects@^1.12.1: + version "1.15.2" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" + integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== + +"forge-std@https://github.com/foundry-rs/forge-std.git#37a3fe48c3a4d8239cda93445f0b5e76b1507436": + version "0.0.0" + resolved "https://github.com/foundry-rs/forge-std.git#37a3fe48c3a4d8239cda93445f0b5e76b1507436" + +fp-ts@1.19.3: + version "1.19.3" + resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-1.19.3.tgz#261a60d1088fbff01f91256f91d21d0caaaaa96f" + integrity sha512-H5KQDspykdHuztLTg+ajGN0Z2qUjcEf3Ybxc6hLt0k7/zPkn29XnKnxlBPyW2XIddWrGaJBzBl4VLYOtk39yZg== + +fp-ts@^1.0.0: + version "1.19.5" + resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-1.19.5.tgz#3da865e585dfa1fdfd51785417357ac50afc520a" + integrity sha512-wDNqTimnzs8QqpldiId9OavWK2NptormjXnRJTQecNjzwfyp6P/8s/zG8e4h3ja3oqkKaY72UlTjQYt/1yXf9A== + +fs-extra@^0.30.0: + version "0.30.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.30.0.tgz#f233ffcc08d4da7d432daa449776989db1df93f0" + integrity sha512-UvSPKyhMn6LEd/WpUaV9C9t3zATuqoqfWc3QdPhPLb58prN9tqYPlPWi8Krxi44loBoUzlobqZ3+8tGpxxSzwA== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^2.1.0" + klaw "^1.0.0" + path-is-absolute "^1.0.0" + rimraf "^2.2.8" + +fs-extra@^7.0.0, fs-extra@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" + integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@~2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g== + +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-intrinsic@^1.0.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.0.tgz#7ad1dc0535f3a2904bba075772763e5051f6d05f" + integrity sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.3" + +glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob@7.1.7: + version "7.1.7" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" + integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.1.2, glob@^7.1.3: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globals@^11.7.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9: + version "4.2.10" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" + integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== + +handlebars@^4.7.7: + version "4.7.7" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" + integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA== + dependencies: + minimist "^1.2.5" + neo-async "^2.6.0" + source-map "^0.6.1" + wordwrap "^1.0.0" + optionalDependencies: + uglify-js "^3.1.4" + +hardhat@^2.10.1: + version "2.12.6" + resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.12.6.tgz#ea3c058bbd81850867389d10f76037cfa52a0019" + integrity sha512-0Ent1O5DsPgvaVb5sxEgsQ3bJRt/Ex92tsoO+xjoNH2Qc4bFmhI5/CHVlFikulalxOPjNmw5XQ2vJFuVQFESAA== + dependencies: + "@ethersproject/abi" "^5.1.2" + "@metamask/eth-sig-util" "^4.0.0" + "@nomicfoundation/ethereumjs-block" "^4.0.0" + "@nomicfoundation/ethereumjs-blockchain" "^6.0.0" + "@nomicfoundation/ethereumjs-common" "^3.0.0" + "@nomicfoundation/ethereumjs-evm" "^1.0.0" + "@nomicfoundation/ethereumjs-rlp" "^4.0.0" + "@nomicfoundation/ethereumjs-statemanager" "^1.0.0" + "@nomicfoundation/ethereumjs-trie" "^5.0.0" + "@nomicfoundation/ethereumjs-tx" "^4.0.0" + "@nomicfoundation/ethereumjs-util" "^8.0.0" + "@nomicfoundation/ethereumjs-vm" "^6.0.0" + "@nomicfoundation/solidity-analyzer" "^0.1.0" + "@sentry/node" "^5.18.1" + "@types/bn.js" "^5.1.0" + "@types/lru-cache" "^5.1.0" + abort-controller "^3.0.0" + adm-zip "^0.4.16" + aggregate-error "^3.0.0" + ansi-escapes "^4.3.0" + chalk "^2.4.2" + chokidar "^3.4.0" + ci-info "^2.0.0" + debug "^4.1.1" + enquirer "^2.3.0" + env-paths "^2.2.0" + ethereum-cryptography "^1.0.3" + ethereumjs-abi "^0.6.8" + find-up "^2.1.0" + fp-ts "1.19.3" + fs-extra "^7.0.1" + glob "7.2.0" + immutable "^4.0.0-rc.12" + io-ts "1.10.4" + keccak "^3.0.2" + lodash "^4.17.11" + mnemonist "^0.38.0" + mocha "^10.0.0" + p-map "^4.0.0" + qs "^6.7.0" + raw-body "^2.4.1" + resolve "1.17.0" + semver "^6.3.0" + solc "0.7.3" + source-map-support "^0.5.13" + stacktrace-parser "^0.1.10" + tsort "0.0.1" + undici "^5.14.0" + uuid "^8.3.2" + ws "^7.4.6" + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +hash-base@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" + integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== + dependencies: + inherits "^2.0.4" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + +he@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +hmac-drbg@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg== + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + +http-errors@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== + dependencies: + depd "2.0.0" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" + +https-proxy-agent@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" + integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== + dependencies: + agent-base "6" + debug "4" + +iconv-lite@0.4.24, iconv-lite@^0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +ieee754@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +ignore@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== + +immutable@^4.0.0-rc.12: + version "4.2.2" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.2.2.tgz#2da9ff4384a4330c36d4d1bc88e90f9e0b0ccd16" + integrity sha512-fTMKDwtbvO5tldky9QZ2fMX7slR0mYpY5nbnFWYp0fOzDhHqhgIw9KoYgxLWsoNTS9ZHGauHj18DTyEw6BK3Og== + +import-fresh@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" + integrity sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg== + dependencies: + caller-path "^2.0.0" + resolve-from "^3.0.0" + +import-fresh@^3.0.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +inquirer@^6.2.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.2.tgz#ad50942375d036d327ff528c08bd5fab089928ca" + integrity sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ== + dependencies: + ansi-escapes "^3.2.0" + chalk "^2.4.2" + cli-cursor "^2.1.0" + cli-width "^2.0.0" + external-editor "^3.0.3" + figures "^2.0.0" + lodash "^4.17.12" + mute-stream "0.0.7" + run-async "^2.2.0" + rxjs "^6.4.0" + string-width "^2.1.0" + strip-ansi "^5.1.0" + through "^2.3.6" + +io-ts@1.10.4: + version "1.10.4" + resolved "https://registry.yarnpkg.com/io-ts/-/io-ts-1.10.4.tgz#cd5401b138de88e4f920adbcb7026e2d1967e6e2" + integrity sha512-b23PteSnYXSONJ6JQXRAlvJhuw8KOtkqa87W4wDtvMrud/DTJd5X+NpOOI+O/zZwVq6v0VLAaJ+1EDViKEuN9g== + dependencies: + fp-ts "^1.0.0" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-buffer@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" + integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== + +is-directory@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" + integrity sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw== + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-hex-prefixed@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz#7d8d37e6ad77e5d127148913c573e082d777f554" + integrity sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA== + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-plain-obj@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" + integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== + +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +js-sha3@0.8.0, js-sha3@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" + integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +js-yaml@^3.12.0, js-yaml@^3.13.0, js-yaml@^3.13.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +json-parse-better-errors@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" + integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + +jsonc-parser@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz#31ff3f4c2b9793f89c67212627c51c6394f88e76" + integrity sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w== + +jsonfile@^2.1.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" + integrity sha512-PKllAqbgLgxHaj8TElYymKCAgrASebJrWpTnEkOaTowt23VKXXN0sUeriJ+eh7y6ufb/CC5ap11pz71/cM0hUw== + optionalDependencies: + graceful-fs "^4.1.6" + +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg== + optionalDependencies: + graceful-fs "^4.1.6" + +keccak@^3.0.0, keccak@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.3.tgz#4bc35ad917be1ef54ff246f904c2bbbf9ac61276" + integrity sha512-JZrLIAJWuZxKbCilMpNz5Vj7Vtb4scDG3dMXLOsbzBmQGyjwE61BbW7bJkfKKCShXiQZt3T6sBgALRtmd+nZaQ== + dependencies: + node-addon-api "^2.0.0" + node-gyp-build "^4.2.0" + readable-stream "^3.6.0" + +klaw@^1.0.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439" + integrity sha512-TED5xi9gGQjGpNnvRWknrwAB1eL5GciPfVFOt3Vk1OJCVDQbzuSfrF3hkUQKlsgKrG1F+0t5W0m+Fje1jIt8rw== + optionalDependencies: + graceful-fs "^4.1.9" + +level-supports@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/level-supports/-/level-supports-4.0.1.tgz#431546f9d81f10ff0fea0e74533a0e875c08c66a" + integrity sha512-PbXpve8rKeNcZ9C1mUicC9auIYFyGpkV9/i6g76tLgANwWhtG2v7I4xNBUlkn3lE2/dZF3Pi0ygYGtLc4RXXdA== + +level-transcoder@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/level-transcoder/-/level-transcoder-1.0.1.tgz#f8cef5990c4f1283d4c86d949e73631b0bc8ba9c" + integrity sha512-t7bFwFtsQeD8cl8NIoQ2iwxA0CL/9IFw7/9gAjOonH0PWTTiRfY7Hq+Ejbsxh86tXobDQ6IOiddjNYIfOBs06w== + dependencies: + buffer "^6.0.3" + module-error "^1.0.1" + +level@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/level/-/level-8.0.0.tgz#41b4c515dabe28212a3e881b61c161ffead14394" + integrity sha512-ypf0jjAk2BWI33yzEaaotpq7fkOPALKAgDBxggO6Q9HGX2MRXn0wbP1Jn/tJv1gtL867+YOjOB49WaUF3UoJNQ== + dependencies: + browser-level "^1.0.1" + classic-level "^1.2.0" + +levn@^0.3.0, levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA== + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +locate-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + integrity sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA== + dependencies: + p-locate "^2.0.0" + path-exists "^3.0.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash.camelcase@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" + integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== + +lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.16: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +log-symbols@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== + dependencies: + chalk "^4.1.0" + is-unicode-supported "^0.1.0" + +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +lru_map@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd" + integrity sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ== + +lunr@^2.3.9: + version "2.3.9" + resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.9.tgz#18b123142832337dd6e964df1a5a7707b25d35e1" + integrity sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow== + +make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + +marked@^4.2.5: + version "4.2.12" + resolved "https://registry.yarnpkg.com/marked/-/marked-4.2.12.tgz#d69a64e21d71b06250da995dcd065c11083bebb5" + integrity sha512-yr8hSKa3Fv4D3jdZmtMMPghgVt6TWbk86WQaWhDloQjRSQhMMYCAro7jP7VDJrjjdV8pxVxMssXS8B8Y5DZ5aw== + +mcl-wasm@^0.7.1: + version "0.7.9" + resolved "https://registry.yarnpkg.com/mcl-wasm/-/mcl-wasm-0.7.9.tgz#c1588ce90042a8700c3b60e40efb339fc07ab87f" + integrity sha512-iJIUcQWA88IJB/5L15GnJVnSQJmf/YaxxV6zRavv83HILHaJQb6y0iFyDMdDO0gN8X37tdxmAOrH/P8B6RB8sQ== + +md5.js@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" + integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +memory-level@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/memory-level/-/memory-level-1.0.0.tgz#7323c3fd368f9af2f71c3cd76ba403a17ac41692" + integrity sha512-UXzwewuWeHBz5krr7EvehKcmLFNoXxGcvuYhC41tRnkrTbJohtS7kVn9akmgirtRygg+f7Yjsfi8Uu5SGSQ4Og== + dependencies: + abstract-level "^1.0.0" + functional-red-black-tree "^1.0.1" + module-error "^1.0.1" + +memorystream@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" + integrity sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw== + +mimic-fn@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" + integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== + +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== + +minimatch@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" + integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^3.0.4, minimatch@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^5.1.2: + version "5.1.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== + dependencies: + brace-expansion "^2.0.1" + +minimist@^1.2.5, minimist@^1.2.6: + version "1.2.7" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" + integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== + +mkdirp@^0.5.1: + version "0.5.6" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" + integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== + dependencies: + minimist "^1.2.6" + +mkdirp@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + +mnemonist@^0.38.0: + version "0.38.5" + resolved "https://registry.yarnpkg.com/mnemonist/-/mnemonist-0.38.5.tgz#4adc7f4200491237fe0fa689ac0b86539685cade" + integrity sha512-bZTFT5rrPKtPJxj8KSV0WkPyNxl72vQepqqVUAW2ARUpUSF2qXMB6jZj7hW5/k7C1rtpzqbD/IIbJwLXUjCHeg== + dependencies: + obliterator "^2.0.0" + +mocha@^10.0.0: + version "10.2.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.2.0.tgz#1fd4a7c32ba5ac372e03a17eef435bd00e5c68b8" + integrity sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg== + dependencies: + ansi-colors "4.1.1" + browser-stdout "1.3.1" + chokidar "3.5.3" + debug "4.3.4" + diff "5.0.0" + escape-string-regexp "4.0.0" + find-up "5.0.0" + glob "7.2.0" + he "1.2.0" + js-yaml "4.1.0" + log-symbols "4.1.0" + minimatch "5.0.1" + ms "2.1.3" + nanoid "3.3.3" + serialize-javascript "6.0.0" + strip-json-comments "3.1.1" + supports-color "8.1.1" + workerpool "6.2.1" + yargs "16.2.0" + yargs-parser "20.2.4" + yargs-unparser "2.0.0" + +module-error@^1.0.1, module-error@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/module-error/-/module-error-1.0.2.tgz#8d1a48897ca883f47a45816d4fb3e3c6ba404d86" + integrity sha512-0yuvsqSCv8LbaOKhnsQ/T5JhyFlCYLPXK3U2sgV10zoKQwzs/MyfuQUOZQ1V/6OCOJsK/TRgNVrPuPDqtdMFtA== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +mute-stream@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" + integrity sha512-r65nCZhrbXXb6dXOACihYApHw2Q6pV0M3V0PSxd74N0+D8nzAdEAITq2oAjA1jVnKI+tGvEBUpqiMh0+rW6zDQ== + +nanoid@3.3.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25" + integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w== + +napi-macros@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/napi-macros/-/napi-macros-2.0.0.tgz#2b6bae421e7b96eb687aa6c77a7858640670001b" + integrity sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + +neo-async@^2.6.0: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== + +node-addon-api@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" + integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== + +node-gyp-build@^4.2.0, node-gyp-build@^4.3.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.6.0.tgz#0c52e4cbf54bbd28b709820ef7b6a3c2d6209055" + integrity sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ== + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +object-inspect@^1.9.0: + version "1.12.3" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" + integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== + +obliterator@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/obliterator/-/obliterator-2.0.4.tgz#fa650e019b2d075d745e44f1effeb13a2adbe816" + integrity sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ== + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +onetime@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" + integrity sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ== + dependencies: + mimic-fn "^1.0.0" + +optionator@^0.8.2: + version "0.8.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.6" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + word-wrap "~1.2.3" + +os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== + +p-limit@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" + integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== + dependencies: + p-try "^1.0.0" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + integrity sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg== + dependencies: + p-limit "^1.1.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +p-map@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== + dependencies: + aggregate-error "^3.0.0" + +p-try@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" + integrity sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww== + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parse-json@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" + integrity sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw== + dependencies: + error-ex "^1.3.1" + json-parse-better-errors "^1.0.1" + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ== + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-is-inside@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" + integrity sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w== + +path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw== + +path-parse@^1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +pbkdf2@^3.0.17: + version "3.1.2" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075" + integrity sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA== + dependencies: + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +picomatch@^2.0.4, picomatch@^2.2.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w== + +prettier-plugin-solidity@^1.0.0-beta.19: + version "1.1.1" + resolved "https://registry.yarnpkg.com/prettier-plugin-solidity/-/prettier-plugin-solidity-1.1.1.tgz#4d3375b85f97812ffcbe48d5a8b3fe914d69c91f" + integrity sha512-uD24KO26tAHF+zMN2nt1OUzfknzza5AgxjogQQrMLZc7j8xiQrDoNWNeOlfFC0YLTwo12CLD10b9niLyP6AqXg== + dependencies: + "@solidity-parser/parser" "^0.14.5" + semver "^7.3.8" + solidity-comments-extractor "^0.0.7" + +prettier@^1.14.3: + version "1.19.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb" + integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew== + +prettier@^2.3.1, prettier@^2.6.2: + version "2.8.3" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.3.tgz#ab697b1d3dd46fb4626fbe2f543afe0cc98d8632" + integrity sha512-tJ/oJ4amDihPoufT5sM0Z1SKEuKay8LfVAMlbbhnnkvt6BUserZylqo2PN+p9KeljLr0OHa2rXHU1T8reeoTrw== + +progress@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + +punycode@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" + integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== + +qs@^6.7.0: + version "6.11.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" + integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== + dependencies: + side-channel "^1.0.4" + +queue-microtask@^1.2.2, queue-microtask@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +raw-body@^2.4.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857" + integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig== + dependencies: + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.4.24" + unpipe "1.0.0" + +readable-stream@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +reduce-flatten@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/reduce-flatten/-/reduce-flatten-2.0.0.tgz#734fd84e65f375d7ca4465c69798c25c9d10ae27" + integrity sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w== + +regexpp@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" + integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + +require-from-string@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + +resolve-from@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" + integrity sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw== + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve@1.17.0: + version "1.17.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" + integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== + dependencies: + path-parse "^1.0.6" + +restore-cursor@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" + integrity sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q== + dependencies: + onetime "^2.0.0" + signal-exit "^3.0.2" + +rimraf@2.6.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== + dependencies: + glob "^7.1.3" + +rimraf@^2.2.8: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + +rlp@^2.2.3: + version "2.2.7" + resolved "https://registry.yarnpkg.com/rlp/-/rlp-2.2.7.tgz#33f31c4afac81124ac4b283e2bd4d9720b30beaf" + integrity sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ== + dependencies: + bn.js "^5.2.0" + +run-async@^2.2.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== + +run-parallel-limit@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/run-parallel-limit/-/run-parallel-limit-1.1.0.tgz#be80e936f5768623a38a963262d6bef8ff11e7ba" + integrity sha512-jJA7irRNM91jaKc3Hcl1npHsFLOXOoTkPCUL1JEa1R82O2miplXXRaGdjW/KM/98YQWDhJLiSs793CnXfblJUw== + dependencies: + queue-microtask "^1.2.2" + +rustbn.js@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/rustbn.js/-/rustbn.js-0.2.0.tgz#8082cb886e707155fd1cb6f23bd591ab8d55d0ca" + integrity sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA== + +rxjs@^6.4.0: + version "6.6.7" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" + integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== + dependencies: + tslib "^1.9.0" + +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +scrypt-js@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-3.0.1.tgz#d314a57c2aef69d1ad98a138a21fe9eafa9ee312" + integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA== + +secp256k1@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-4.0.3.tgz#c4559ecd1b8d3c1827ed2d1b94190d69ce267303" + integrity sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA== + dependencies: + elliptic "^6.5.4" + node-addon-api "^2.0.0" + node-gyp-build "^4.2.0" + +semver@^5.5.0, semver@^5.5.1: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +semver@^7.3.8: + version "7.3.8" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" + integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== + dependencies: + lru-cache "^6.0.0" + +serialize-javascript@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" + integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== + dependencies: + randombytes "^2.1.0" + +setimmediate@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== + +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +sha.js@^2.4.0, sha.js@^2.4.8: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg== + dependencies: + shebang-regex "^1.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ== + +shiki@^0.12.1: + version "0.12.1" + resolved "https://registry.yarnpkg.com/shiki/-/shiki-0.12.1.tgz#26fce51da12d055f479a091a5307470786f300cd" + integrity sha512-aieaV1m349rZINEBkjxh2QbBvFFQOlgqYTNtCal82hHj4dDZ76oMlQIX+C7ryerBTDiga3e5NfH6smjdJ02BbQ== + dependencies: + jsonc-parser "^3.2.0" + vscode-oniguruma "^1.7.0" + vscode-textmate "^8.0.0" + +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" + +signal-exit@^3.0.2: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +slice-ansi@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" + integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== + dependencies: + ansi-styles "^3.2.0" + astral-regex "^1.0.0" + is-fullwidth-code-point "^2.0.0" + +solc@0.7.3: + version "0.7.3" + resolved "https://registry.yarnpkg.com/solc/-/solc-0.7.3.tgz#04646961bd867a744f63d2b4e3c0701ffdc7d78a" + integrity sha512-GAsWNAjGzIDg7VxzP6mPjdurby3IkGCjQcM8GFYZT6RyaoUZKmMU6Y7YwG+tFGhv7dwZ8rmR4iwFDrrD99JwqA== + dependencies: + command-exists "^1.2.8" + commander "3.0.2" + follow-redirects "^1.12.1" + fs-extra "^0.30.0" + js-sha3 "0.8.0" + memorystream "^0.3.1" + require-from-string "^2.0.0" + semver "^5.5.0" + tmp "0.0.33" + +solhint@^3.3.7: + version "3.3.8" + resolved "https://registry.yarnpkg.com/solhint/-/solhint-3.3.8.tgz#b1773c881cfaf0b5008c78ad658a69603d3fa051" + integrity sha512-TkYyJ6uUJCaiqRKuhHhFuoAoyco9Ia+RDKhl3usjG/rkaNk8/LdLRla2Xln7MVdBTaPKNAU8ezTRSit50Yy4qw== + dependencies: + "@solidity-parser/parser" "^0.14.5" + ajv "^6.6.1" + antlr4 "4.7.1" + ast-parents "0.0.1" + chalk "^2.4.2" + commander "2.18.0" + cosmiconfig "^5.0.7" + eslint "^5.6.0" + fast-diff "^1.1.2" + glob "^7.1.3" + ignore "^4.0.6" + js-yaml "^3.12.0" + lodash "^4.17.11" + semver "^6.3.0" + optionalDependencies: + prettier "^1.14.3" + +solidity-ast@^0.4.38: + version "0.4.44" + resolved "https://registry.yarnpkg.com/solidity-ast/-/solidity-ast-0.4.44.tgz#dd6732bd65bb1d01777fc537de99cbb3d4e0089d" + integrity sha512-Ct3ppqWS0uTWNYxM2cgruUeWYzqYmeikANsCHgGBnMjAMsqONgqnYrlpifQxNFwXOPHD3vZQLmCjaYnQ+i3eQA== + +solidity-comments-extractor@^0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/solidity-comments-extractor/-/solidity-comments-extractor-0.0.7.tgz#99d8f1361438f84019795d928b931f4e5c39ca19" + integrity sha512-wciNMLg/Irp8OKGrh3S2tfvZiZ0NEyILfcRCXCD4mp7SgK/i9gzLfhY2hY7VMCQJ3kH9UB9BzNdibIVMchzyYw== + +solidity-docgen@^0.6.0-beta.22: + version "0.6.0-beta.34" + resolved "https://registry.yarnpkg.com/solidity-docgen/-/solidity-docgen-0.6.0-beta.34.tgz#f1766b13ea864ea71b8e727796d30a69ea90014a" + integrity sha512-igdGrkg8gT1jn+B2NwzjEtSf+7NTrSi/jz88zO7MZWgETmcWbXaxgAsQP4BQeC4YFeH0Pie1NsLP7+9qDgvFtA== + dependencies: + handlebars "^4.7.7" + solidity-ast "^0.4.38" + +source-map-support@^0.5.13: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0, source-map@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== + +stacktrace-parser@^0.1.10: + version "0.1.10" + resolved "https://registry.yarnpkg.com/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz#29fb0cae4e0d0b85155879402857a1639eb6051a" + integrity sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg== + dependencies: + type-fest "^0.7.1" + +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== + +streamsearch@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" + integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== + +string-format@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/string-format/-/string-format-2.0.0.tgz#f2df2e7097440d3b65de31b6d40d54c96eaffb9b" + integrity sha512-bbEs3scLeYNXLecRRuk6uJxdXUSj6le/8rNPHChIJTn2V79aXVTR1EH2OH5zLKKoz0V02fOUKZZcw01pLUShZA== + +string-width@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string-width@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + +string-width@^4.1.0, string-width@^4.2.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow== + dependencies: + ansi-regex "^3.0.0" + +strip-ansi@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-hex-prefix@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz#0c5f155fef1151373377de9dbb588da05500e36f" + integrity sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A== + dependencies: + is-hex-prefixed "1.0.0" + +strip-json-comments@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +strip-json-comments@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== + +supports-color@8.1.1: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +table-layout@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/table-layout/-/table-layout-1.0.2.tgz#c4038a1853b0136d63365a734b6931cf4fad4a04" + integrity sha512-qd/R7n5rQTRFi+Zf2sk5XVVd9UQl6ZkduPFC3S7WEGJAmetDTjY3qPN50eSKzwuzEyQKy5TN2TiZdkIjos2L6A== + dependencies: + array-back "^4.0.1" + deep-extend "~0.6.0" + typical "^5.2.0" + wordwrapjs "^4.0.0" + +table@^5.2.3: + version "5.4.6" + resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" + integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== + dependencies: + ajv "^6.10.2" + lodash "^4.17.14" + slice-ansi "^2.1.0" + string-width "^3.0.0" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + +through@^2.3.6: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== + +tmp@0.0.33, tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + +ts-command-line-args@^2.2.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/ts-command-line-args/-/ts-command-line-args-2.4.2.tgz#b4815b23c35f8a0159d4e69e01012d95690bc448" + integrity sha512-mJLQQBOdyD4XI/ZWQY44PIdYde47JhV2xl380O7twPkTQ+Y5vFDHsk8LOeXKuz7dVY5aDCfAzRarNfSqtKOkQQ== + dependencies: + "@morgan-stanley/ts-mocking-bird" "^0.6.2" + chalk "^4.1.0" + command-line-args "^5.1.1" + command-line-usage "^6.1.0" + string-format "^2.0.0" + +ts-essentials@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-7.0.3.tgz#686fd155a02133eedcc5362dc8b5056cde3e5a38" + integrity sha512-8+gr5+lqO3G84KdiTSMRLtuyJ+nTBVRKuCrK4lidMPdVeEp0uqC875uE5NMcaA7YYMN7XsNiFQuMvasF8HT/xQ== + +ts-node@10.7: + version "10.7.0" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.7.0.tgz#35d503d0fab3e2baa672a0e94f4b40653c2463f5" + integrity sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A== + dependencies: + "@cspotcode/source-map-support" "0.7.0" + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + v8-compile-cache-lib "^3.0.0" + yn "3.1.1" + +tslib@^1.9.0, tslib@^1.9.3: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tsort@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/tsort/-/tsort-0.0.1.tgz#e2280f5e817f8bf4275657fd0f9aebd44f5a2786" + integrity sha512-Tyrf5mxF8Ofs1tNoxA13lFeZ2Zrbd6cKbuH3V+MQ5sb6DtBj5FjrXVsRWT8YvNAQTqNoz66dz1WsbigI22aEnw== + +tweetnacl-util@^0.15.1: + version "0.15.1" + resolved "https://registry.yarnpkg.com/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz#b80fcdb5c97bcc508be18c44a4be50f022eea00b" + integrity sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw== + +tweetnacl@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" + integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg== + dependencies: + prelude-ls "~1.1.2" + +type-fest@^0.21.3: + version "0.21.3" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== + +type-fest@^0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.7.1.tgz#8dda65feaf03ed78f0a3f9678f1869147f7c5c48" + integrity sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg== + +typechain@^8.1.1: + version "8.1.1" + resolved "https://registry.yarnpkg.com/typechain/-/typechain-8.1.1.tgz#9c2e8012c2c4c586536fc18402dcd7034c4ff0bd" + integrity sha512-uF/sUvnXTOVF2FHKhQYnxHk4su4JjZR8vr4mA2mBaRwHTbwh0jIlqARz9XJr1tA0l7afJGvEa1dTSi4zt039LQ== + dependencies: + "@types/prettier" "^2.1.1" + debug "^4.3.1" + fs-extra "^7.0.0" + glob "7.1.7" + js-sha3 "^0.8.0" + lodash "^4.17.15" + mkdirp "^1.0.4" + prettier "^2.3.1" + ts-command-line-args "^2.2.0" + ts-essentials "^7.0.1" + +typedoc@^0.23.10: + version "0.23.24" + resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.23.24.tgz#01cf32c09f2c19362e72a9ce1552d6e5b48c4fef" + integrity sha512-bfmy8lNQh+WrPYcJbtjQ6JEEsVl/ce1ZIXyXhyW+a1vFrjO39t6J8sL/d6FfAGrJTc7McCXgk9AanYBSNvLdIA== + dependencies: + lunr "^2.3.9" + marked "^4.2.5" + minimatch "^5.1.2" + shiki "^0.12.1" + +typical@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/typical/-/typical-4.0.0.tgz#cbeaff3b9d7ae1e2bbfaf5a4e6f11eccfde94fc4" + integrity sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw== + +typical@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/typical/-/typical-5.2.0.tgz#4daaac4f2b5315460804f0acf6cb69c52bb93066" + integrity sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg== + +uglify-js@^3.1.4: + version "3.17.4" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.17.4.tgz#61678cf5fa3f5b7eb789bb345df29afb8257c22c" + integrity sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g== + +undici@^5.14.0: + version "5.16.0" + resolved "https://registry.yarnpkg.com/undici/-/undici-5.16.0.tgz#6b64f9b890de85489ac6332bd45ca67e4f7d9943" + integrity sha512-KWBOXNv6VX+oJQhchXieUznEmnJMqgXMbs0xxH2t8q/FUAWSJvOSr/rMaZKnX5RIVq7JDn0JbP4BOnKG2SGXLQ== + dependencies: + busboy "^1.6.0" + +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + +unpipe@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +util-deprecate@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +uuid@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b" + integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg== + +uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + +v8-compile-cache-lib@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" + integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== + +vscode-oniguruma@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz#439bfad8fe71abd7798338d1cd3dc53a8beea94b" + integrity sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA== + +vscode-textmate@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-8.0.0.tgz#2c7a3b1163ef0441097e0b5d6389cd5504b59e5d" + integrity sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg== + +which@^1.2.9: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +word-wrap@~1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + +wordwrap@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== + +wordwrapjs@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/wordwrapjs/-/wordwrapjs-4.0.1.tgz#d9790bccfb110a0fc7836b5ebce0937b37a8b98f" + integrity sha512-kKlNACbvHrkpIw6oPeYDSmdCTu2hdMHoyXLTcUKala++lx5Y+wjJ/e474Jqv5abnVmwxw08DiTuHmw69lJGksA== + dependencies: + reduce-flatten "^2.0.0" + typical "^5.2.0" + +workerpool@6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" + integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +write@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" + integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== + dependencies: + mkdirp "^0.5.1" + +ws@^7.4.6: + version "7.5.9" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" + integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yargs-parser@20.2.4: + version "20.2.4" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" + integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== + +yargs-parser@^20.2.2: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + +yargs-unparser@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" + integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== + dependencies: + camelcase "^6.0.0" + decamelize "^4.0.0" + flat "^5.0.2" + is-plain-obj "^2.1.0" + +yargs@16.2.0: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== From c6a6fa38cebedcd3591e72b8ca24d68371de7b6b Mon Sep 17 00:00:00 2001 From: alvrs Date: Tue, 31 Jan 2023 22:01:58 +0100 Subject: [PATCH 34/82] build(store): add -rf to dist command to override existing files --- packages/store/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/store/package.json b/packages/store/package.json index 175ffc4d1c..340c99b752 100644 --- a/packages/store/package.json +++ b/packages/store/package.json @@ -14,7 +14,7 @@ "git:install": "bash git-install.sh", "test": "forge test", "build": "rimraf out && forge build --out out && yarn dist && yarn types", - "dist": "rimraf abi && mkdir abi && cp out/*.sol/*.json abi/ && yarn rimraf abi/*.metadata.json", + "dist": "rimraf abi && mkdir abi && cp -rf out/*.sol/*.json abi/ && yarn rimraf abi/*.metadata.json", "types": "rimraf types && typechain --target=ethers-v5 abi/*.json", "prettier": "prettier --write 'src/**/*.sol'", "solhint": "solhint --config ./.solhint.json 'src/**/*.sol'", From e9f97c66557f958ef0165f32d9b1343ab681dcfd Mon Sep 17 00:00:00 2001 From: alvrs Date: Wed, 1 Feb 2023 16:33:25 +0100 Subject: [PATCH 35/82] test(store): generate gasreport --- packages/cli/src/commands/gas-report.ts | 4 + packages/solecs/gas-report.txt | 21 --- packages/store/gas-report.txt | 145 +++++++++++++++++--- packages/store/src/tables/Vector2Table.sol | 2 +- packages/store/src/test/Gas.t.sol | 10 +- packages/store/src/test/MixedTable.t.sol | 26 ++-- packages/store/src/test/PackedCounter.t.sol | 21 +-- packages/store/src/test/RouteTable.t.sol | 12 +- packages/store/src/test/Schema.t.sol | 39 ++++-- packages/store/src/test/Storage.t.sol | 19 +-- packages/store/src/test/StoreCore.t.sol | 100 ++++---------- packages/store/src/test/StoreSwitch.t.sol | 4 +- packages/store/src/test/System.t.sol | 4 +- packages/store/src/test/Vector2Table.t.sol | 12 +- packages/store/src/test/World.t.sol | 10 +- 15 files changed, 217 insertions(+), 212 deletions(-) delete mode 100644 packages/solecs/gas-report.txt diff --git a/packages/cli/src/commands/gas-report.ts b/packages/cli/src/commands/gas-report.ts index 6de17786f5..89a723cd5b 100644 --- a/packages/cli/src/commands/gas-report.ts +++ b/packages/cli/src/commands/gas-report.ts @@ -98,6 +98,10 @@ export const handler = async (args: Arguments): Promise => { }; async function runGasReport(path: string): Promise { + if (!path.endsWith(".t.sol")) { + console.log("Skipping gas report for", chalk.bold(path), "(not a test file)"); + return []; + } console.log("Running gas report for", chalk.bold(path)); const gasReport: GasReport = []; diff --git a/packages/solecs/gas-report.txt b/packages/solecs/gas-report.txt deleted file mode 100644 index d9df7b16e0..0000000000 --- a/packages/solecs/gas-report.txt +++ /dev/null @@ -1,21 +0,0 @@ -(v2/test/Buffer.t.sol) | allocate a buffer [Buffer buf = Buffer_.allocate(32)]: 73 -(v2/test/Buffer.t.sol) | get buffer length [buf.length()]: 87 -(v2/test/Buffer.t.sol) | get buffer pointer [buf.ptr()]: 33 -(v2/test/Buffer.t.sol) | get buffer capacity [buf.capacity()]: 7 -(v2/test/Buffer.t.sol) | append unchecked bytes memory (8) to buffer [buf.appendUnchecked(data1)]: 478 -(v2/test/Buffer.t.sol) | append bytes memory (8) to buffer [buf.append(data2)]: 773 -(v2/test/Buffer.t.sol) | append unchecked bytes8 of bytes32 to buffer [buf.appendUnchecked(data1, 8)]: 351 -(v2/test/Buffer.t.sol) | append bytes8 of bytes32 to buffer [buf.append(data2, 8)]: 645 -(v2/test/Buffer.t.sol) | concat 3 bytes memory (32) using buffer [Buffer buf = Buffer_.concat(data1, data2, data3)]: 2638 -(v2/test/Buffer.t.sol) | concat 3 bytes memory (32) using bytes.concat [bytes memory concat = bytes.concat(data1, data2, data3)]: 641 -(v2/test/Buffer.t.sol) | create a buffer from 8 bytes [Buffer buf = Buffer_.fromBytes(data)]: 40 -(v2/test/Buffer.t.sol) | read bytes32 from buffer [bytes32 value = buf.read32(4)]: 102 -(v2/test/Buffer.t.sol) | read bytes8 with offset 3 from buffer [bytes8 value2 = buf.read8(3)]: 151 -(v2/test/Buffer.t.sol) | read bytes1 with offset 7 from buffer [bytes1 value3 = buf.read1(7)]: 151 -(v2/test/Buffer.t.sol) | set buffer length unchecked [buf._setLengthUnchecked(8)]: 86 -(v2/test/Buffer.t.sol) | set buffer length [buf._setLength(16)]: 192 -(v2/test/Buffer.t.sol) | slice 4 bytes from buffer with offset 4 [bytes memory slice = buf.slice(4, 4)]: 635 -(v2/test/Buffer.t.sol) | convert array pointer to uint256[] [uint256[] memory arr = Cast.toUint256Array(arrayPtr)]: 10 -(v2/test/Buffer.t.sol) | buffer toArray with element length 4 [uint256 arrayPtr = buf.toArray(4)]: 663 -(v2/test/Buffer.t.sol) | convert array pointer to uint32[] [uint32[] memory arr = Cast.toUint32Array(arrayPtr)]: 10 -(v2/test/Buffer.t.sol) | buffer (32 bytes) to bytes memory [bytes memory bufferData = buf.toBytes()]: 90 \ No newline at end of file diff --git a/packages/store/gas-report.txt b/packages/store/gas-report.txt index d9df7b16e0..887c3f0743 100644 --- a/packages/store/gas-report.txt +++ b/packages/store/gas-report.txt @@ -1,21 +1,124 @@ -(v2/test/Buffer.t.sol) | allocate a buffer [Buffer buf = Buffer_.allocate(32)]: 73 -(v2/test/Buffer.t.sol) | get buffer length [buf.length()]: 87 -(v2/test/Buffer.t.sol) | get buffer pointer [buf.ptr()]: 33 -(v2/test/Buffer.t.sol) | get buffer capacity [buf.capacity()]: 7 -(v2/test/Buffer.t.sol) | append unchecked bytes memory (8) to buffer [buf.appendUnchecked(data1)]: 478 -(v2/test/Buffer.t.sol) | append bytes memory (8) to buffer [buf.append(data2)]: 773 -(v2/test/Buffer.t.sol) | append unchecked bytes8 of bytes32 to buffer [buf.appendUnchecked(data1, 8)]: 351 -(v2/test/Buffer.t.sol) | append bytes8 of bytes32 to buffer [buf.append(data2, 8)]: 645 -(v2/test/Buffer.t.sol) | concat 3 bytes memory (32) using buffer [Buffer buf = Buffer_.concat(data1, data2, data3)]: 2638 -(v2/test/Buffer.t.sol) | concat 3 bytes memory (32) using bytes.concat [bytes memory concat = bytes.concat(data1, data2, data3)]: 641 -(v2/test/Buffer.t.sol) | create a buffer from 8 bytes [Buffer buf = Buffer_.fromBytes(data)]: 40 -(v2/test/Buffer.t.sol) | read bytes32 from buffer [bytes32 value = buf.read32(4)]: 102 -(v2/test/Buffer.t.sol) | read bytes8 with offset 3 from buffer [bytes8 value2 = buf.read8(3)]: 151 -(v2/test/Buffer.t.sol) | read bytes1 with offset 7 from buffer [bytes1 value3 = buf.read1(7)]: 151 -(v2/test/Buffer.t.sol) | set buffer length unchecked [buf._setLengthUnchecked(8)]: 86 -(v2/test/Buffer.t.sol) | set buffer length [buf._setLength(16)]: 192 -(v2/test/Buffer.t.sol) | slice 4 bytes from buffer with offset 4 [bytes memory slice = buf.slice(4, 4)]: 635 -(v2/test/Buffer.t.sol) | convert array pointer to uint256[] [uint256[] memory arr = Cast.toUint256Array(arrayPtr)]: 10 -(v2/test/Buffer.t.sol) | buffer toArray with element length 4 [uint256 arrayPtr = buf.toArray(4)]: 663 -(v2/test/Buffer.t.sol) | convert array pointer to uint32[] [uint32[] memory arr = Cast.toUint32Array(arrayPtr)]: 10 -(v2/test/Buffer.t.sol) | buffer (32 bytes) to bytes memory [bytes memory bufferData = buf.toBytes()]: 90 \ No newline at end of file +(src/test/Buffer.t.sol) | allocate a buffer [Buffer buf = Buffer_.allocate(32)]: 73 +(src/test/Buffer.t.sol) | get buffer length [buf.length()]: 87 +(src/test/Buffer.t.sol) | get buffer pointer [buf.ptr()]: 33 +(src/test/Buffer.t.sol) | get buffer capacity [buf.capacity()]: 7 +(src/test/Buffer.t.sol) | append unchecked bytes memory (8) to buffer [buf.appendUnchecked(data1)]: 478 +(src/test/Buffer.t.sol) | append bytes memory (8) to buffer [buf.append(data2)]: 773 +(src/test/Buffer.t.sol) | append unchecked bytes8 of bytes32 to buffer [buf.appendUnchecked(data1, 8)]: 351 +(src/test/Buffer.t.sol) | append bytes8 of bytes32 to buffer [buf.append(data2, 8)]: 645 +(src/test/Buffer.t.sol) | concat 3 bytes memory (32) using buffer [Buffer buf = Buffer_.concat(data1, data2, data3)]: 2638 +(src/test/Buffer.t.sol) | concat 3 bytes memory (32) using bytes.concat [bytes memory concat = bytes.concat(data1, data2, data3)]: 641 +(src/test/Buffer.t.sol) | create a buffer from 8 bytes [Buffer buf = Buffer_.fromBytes(data)]: 40 +(src/test/Buffer.t.sol) | read bytes32 from buffer [bytes32 value = buf.read32(4)]: 102 +(src/test/Buffer.t.sol) | read bytes8 with offset 3 from buffer [bytes8 value2 = buf.read8(3)]: 151 +(src/test/Buffer.t.sol) | read bytes1 with offset 7 from buffer [bytes1 value3 = buf.read1(7)]: 151 +(src/test/Buffer.t.sol) | set buffer length unchecked [buf._setLengthUnchecked(8)]: 86 +(src/test/Buffer.t.sol) | set buffer length [buf._setLength(16)]: 192 +(src/test/Buffer.t.sol) | slice 4 bytes from buffer with offset 4 [bytes memory slice = buf.slice(4, 4)]: 635 +(src/test/Buffer.t.sol) | convert array pointer to uint256[] [uint256[] memory arr = Cast.toUint256Array(arrayPtr)]: 10 +(src/test/Buffer.t.sol) | buffer toArray with element length 4 [uint256 arrayPtr = buf.toArray(4)]: 745 +(src/test/Buffer.t.sol) | convert array pointer to uint32[] [uint32[] memory arr = Cast.toUint32Array(arrayPtr)]: 10 +(src/test/Buffer.t.sol) | buffer (32 bytes) to bytes memory [bytes memory bufferData = buf.toBytes()]: 90 +(src/test/Bytes.t.sol) | compare equal bytes [bool equals = Bytes.equals(a, b)]: 202 +(src/test/Bytes.t.sol) | compare unequal bytes [bool equals = Bytes.equals(a, b)]: 202 +(src/test/Bytes.t.sol) | create bytes from address [bytes memory output = Bytes.from(input)]: 157 +(src/test/Bytes.t.sol) | create address from bytes [address output2 = Bytes.toAddress(output)]: 166 +(src/test/Bytes.t.sol) | create bytes from bytes4 [bytes memory output = Bytes.from(input)]: 151 +(src/test/Bytes.t.sol) | create bytes4 from bytes [bytes4 output2 = Bytes.toBytes4(output)]: 160 +(src/test/Bytes.t.sol) | create bytes from uint32 [bytes memory output = Bytes.from(input)]: 149 +(src/test/Bytes.t.sol) | create uint32 from bytes [uint32 output2 = Bytes.toUint32(output)]: 166 +(src/test/Bytes.t.sol) | create uint32 array from bytes memory [uint32[] memory output = Bytes.toUint32Array(tight)]: 835 +(src/test/Bytes.t.sol) | create bytes from uint8 [bytes memory output = Bytes.fromUint8(input)]: 160 +(src/test/Bytes.t.sol) | create uint8 from bytes [uint8 output2 = Bytes.toUint8(output)]: 105 +(src/test/Bytes.t.sol) | create bytes from bytes array [bytes memory output = Bytes.from(input)]: 1290 +(src/test/Bytes.t.sol) | create bytes from uint16 array [bytes memory output = Bytes.from(input)]: 791 +(src/test/Bytes.t.sol) | create bytes from uint32 array [bytes memory output = Bytes.from(input)]: 695 +(src/test/Bytes.t.sol) | create bytes from uint8 array [bytes memory output = Bytes.from(input)]: 695 +(src/test/Bytes.t.sol) | set bytes1 in bytes32 [Bytes.setBytes1(input, 8, 0xff)]: 7 +(src/test/Bytes.t.sol) | set bytes2 in bytes32 [Bytes.setBytes2(input, 8, 0xffff)]: 7 +(src/test/Bytes.t.sol) | set bytes4 in bytes32 [Bytes.setBytes4(input, 8, 0xffffffff)]: 7 +(src/test/Bytes.t.sol) | set length of bytes in place [Bytes.setLengthInPlace(a, 2)]: 16 +(src/test/Bytes.t.sol) | slice bytes (with copying) with offset 1 and length 3 [bytes memory b = Bytes.slice(a, 1, 3)]: 516 +(src/test/Bytes.t.sol) | slice bytes3 with offset 1 [bytes3 b = Bytes.slice3(a, 1)]: 77 +(src/test/Bytes.t.sol) | slice bytes32 with offset 10 [bytes32 output = Bytes.slice32(input, 10)]: 74 +(src/test/Bytes.t.sol) | tightly pack bytes24 array into bytes array [bytes memory tight = Bytes.from(input)]: 477 +(src/test/Bytes.t.sol) | create uint32 array from bytes memory [bytes24[] memory output = Bytes.toBytes24Array(tight)]: 614 +(src/test/Bytes.t.sol) | create bytes32 from bytes memory with offset 0 [bytes32 output = Bytes.toBytes32(input, 0)]: 22 +(src/test/Bytes.t.sol) | create bytes32 array from bytes memory [bytes32[] memory output = Bytes.toBytes32Array(input)]: 1110 +(src/test/Bytes.t.sol) | create bytes32 array from bytes memory with uneven length [bytes32[] memory output = Bytes.toBytes32Array(input)]: 1425 +(src/test/Bytes.t.sol) | create bytes32 from bytes memory with offset 16 [bytes32 output = Bytes.toBytes32(input, 16)]: 22 +(src/test/Gas.t.sol) | abi encode [bytes memory abiEncoded = abi.encode(mixed)]: 930 +(src/test/Gas.t.sol) | abi decode [Mixed memory abiDecoded = abi.decode(abiEncoded, (Mixed))]: 1713 +(src/test/Gas.t.sol) | custom encode [bytes memory customEncoded = customEncode(mixed)]: 1393 +(src/test/Gas.t.sol) | custom decode [Mixed memory customDecoded = customDecode(customEncoded)]: 2772 +(src/test/Gas.t.sol) | pass abi encoded bytes to external contract [someContract.doSomethingWithBytes(abiEncoded)]: 6537 +(src/test/Gas.t.sol) | pass custom encoded bytes to external contract [someContract.doSomethingWithBytes(customEncoded)]: 1342 +(src/test/MixedTable.t.sol) | store Mixed struct in storage (native solidity) [testMixed = mixed]: 92016 +(src/test/MixedTable.t.sol) | register MixedTable schema [MixedTable.registerSchema()]: 32485 +(src/test/MixedTable.t.sol) | set record in MixedTable [MixedTable.set({ key: key, u32: 1, u128: 2, a32: a32, s: s })]: 114636 +(src/test/MixedTable.t.sol) | get record from MixedTable [Mixed memory mixed = MixedTable.get(key)]: 20494 +(src/test/PackedCounter.t.sol) | get value at index of PackedCounter [packedCounter.atIndex(3)]: 272 +(src/test/PackedCounter.t.sol) | set value at index of PackedCounter [packedCounter = packedCounter.setAtIndex(2, 5)]: 884 +(src/test/PackedCounter.t.sol) | pack uint16 array into PackedCounter [PackedCounter packedCounter = PackedCounter_.pack(counters)]: 2148 +(src/test/PackedCounter.t.sol) | get total of PackedCounter [packedCounter.total()]: 33 +(src/test/RouteTable.t.sol) | register RouteTable schema [RouteTable.registerSchema()]: 30440 +(src/test/RouteTable.t.sol) | set RouteTable record [RouteTable.set(key, addr, selector, executionMode)]: 29726 +(src/test/RouteTable.t.sol) | get RouteTable record [Route memory systemEntry = RouteTable.get(key)]: 6506 +(src/test/Schema.t.sol) | encode schema with 6 entries [Schema_.encode]: 6202 +(src/test/Schema.t.sol) | get schema type at index [SchemaType schemaType1 = schema.atIndex(0)]: 200 +(src/test/Schema.t.sol) | get number of dynamic fields from schema [uint256 num = schema.numDynamicFields()]: 80 +(src/test/Schema.t.sol) | get number of static fields from schema [uint256 num = schema.numStaticFields()]: 91 +(src/test/Schema.t.sol) | get static data length from schema [uint256 length = schema.staticDataLength()]: 39 +(src/test/Schema.t.sol) | check if schema is empty (non-empty schema) [bool empty = encodedSchema.isEmpty()]: 13 +(src/test/Schema.t.sol) | check if schema is empty (empty schema) [bool empty = encodedSchema.isEmpty()]: 13 +(src/test/Schema.t.sol) | validate schema [encodedSchema.validate()]: 22716 +(src/test/Storage.t.sol) | write 1 storage slot [Storage.write(storagePointer, originalDataFirstSlot)]: 23509 +(src/test/Storage.t.sol) | write 34 bytes over 3 storage slots (with offset and safeTrail)) [Storage.write(storagePointer, 31, data1)]: 25749 +(src/test/Storage.t.sol) | read 34 bytes over 3 storage slots (with offset and safeTrail)) [bytes memory data = Storage.read(storagePointer, 31, 34)]: 4131 +(src/test/StoreCore.t.sol) | access non-existing record [bytes memory data1 = StoreCore.getRecord(table, key)]: 10041 +(src/test/StoreCore.t.sol) | access static field of non-existing record [bytes memory data2 = StoreCore.getField(table, key, 0)]: 4459 +(src/test/StoreCore.t.sol) | access dynamic field of non-existing record [bytes memory data3 = StoreCore.getField(table, key, 1)]: 3891 +(src/test/StoreCore.t.sol) | delete record (complex data, 3 slots) [StoreCore.deleteRecord(table, key)]: 13802 +(src/test/StoreCore.t.sol) | Check for existence of table (existent) [StoreCore.hasTable(table)]: 949 +(src/test/StoreCore.t.sol) | check for existence of table (non-existent) [StoreCore.hasTable(table2)]: 2950 +(src/test/StoreCore.t.sol) | register subscriber [StoreCore.registerOnUpdateHook(table, subscriber)]: 67055 +(src/test/StoreCore.t.sol) | set record on table with subscriber [StoreCore.setRecord(table, key, data)]: 72149 +(src/test/StoreCore.t.sol) | set field on table with subscriber [StoreCore.setField(table, key, 0, data)]: 34267 +(src/test/StoreCore.t.sol) | register subscriber [StoreCore.registerOnUpdateHook(table, subscriber)]: 67055 +(src/test/StoreCore.t.sol) | set record on table with subscriber [StoreCore.setRecord(table, key, data)]: 175065 +(src/test/StoreCore.t.sol) | set field on table with subscriber [StoreCore.setField(table, key, 1, arrayDataBytes)]: 37164 +(src/test/StoreCore.t.sol) | StoreCore: register schema [StoreCore.registerSchema(table, schema)]: 26373 +(src/test/StoreCore.t.sol) | StoreCore: get schema (warm) [Schema loadedSchema = StoreCore.getSchema(table)]: 894 +(src/test/StoreCore.t.sol) | set complex record with dynamic data (4 slots) [StoreCore.setRecord(table, key, data)]: 110840 +(src/test/StoreCore.t.sol) | get complex record with dynamic data (4 slots) [bytes memory loadedData = StoreCore.getRecord(table, key)]: 12700 +(src/test/StoreCore.t.sol) | compare: Set complex record with dynamic data using native solidity [testStruct = _testStruct]: 116815 +(src/test/StoreCore.t.sol) | compare: Set complex record with dynamic data using abi.encode [testMapping[1234] = abi.encode(_testStruct)]: 267529 +(src/test/StoreCore.t.sol) | set dynamic length of dynamic index 0 [StoreCore._setDynamicDataLengthAtIndex(table, key, 0, 10)]: 23716 +(src/test/StoreCore.t.sol) | set dynamic length of dynamic index 1 [StoreCore._setDynamicDataLengthAtIndex(table, key, 1, 99)]: 1817 +(src/test/StoreCore.t.sol) | reduce dynamic length of dynamic index 0 [StoreCore._setDynamicDataLengthAtIndex(table, key, 0, 5)]: 1818 +(src/test/StoreCore.t.sol) | set static field (1 slot) [StoreCore.setField(table, key, 0, bytes.concat(firstDataBytes))]: 38119 +(src/test/StoreCore.t.sol) | get static field (1 slot) [bytes memory loadedData = StoreCore.getField(table, key, 0)]: 4549 +(src/test/StoreCore.t.sol) | set static field (overlap 2 slot) [StoreCore.setField(table, key, 1, bytes.concat(secondDataBytes))]: 33949 +(src/test/StoreCore.t.sol) | get static field (overlap 2 slot) [loadedData = StoreCore.getField(table, key, 1)]: 6333 +(src/test/StoreCore.t.sol) | set dynamic field (1 slot, first dynamic field) [StoreCore.setField(table, key, 2, thirdDataBytes)]: 57392 +(src/test/StoreCore.t.sol) | get dynamic field (1 slot, first dynamic field) [loadedData = StoreCore.getField(table, key, 2)]: 5308 +(src/test/StoreCore.t.sol) | set dynamic field (1 slot, second dynamic field) [StoreCore.setField(table, key, 3, fourthDataBytes)]: 35526 +(src/test/StoreCore.t.sol) | get dynamic field (1 slot, second dynamic field) [loadedData = StoreCore.getField(table, key, 3)]: 5323 +(src/test/StoreCore.t.sol) | set static record (1 slot) [StoreCore.setStaticData(table, key, data)]: 28620 +(src/test/StoreCore.t.sol) | get static record (1 slot) [bytes memory loadedData = StoreCore.getRecord(table, key, schema)]: 4098 +(src/test/StoreCore.t.sol) | set static record (2 slots) [StoreCore.setStaticData(table, key, data)]: 51561 +(src/test/StoreCore.t.sol) | get static record (2 slots) [bytes memory loadedData = StoreCore.getRecord(table, key, schema)]: 4987 +(src/test/StoreSwitch.t.sol) | check if delegatecall [isDelegate = StoreSwitch.isDelegateCall()]: 671 +(src/test/StoreSwitch.t.sol) | check if delegatecall [isDelegate = StoreSwitch.isDelegateCall()]: 627 +(src/test/System.t.sol) | extract msg.sender from calldata [address sender = _msgSender()]: 24 +(src/test/Vector2Table.t.sol) | register Vector2Table schema [Vector2Table.registerSchema()]: 28662 +(src/test/Vector2Table.t.sol) | set Vector2Table record [Vector2Table.set({ key: key, x: 1, y: 2 })]: 29689 +(src/test/Vector2Table.t.sol) | get Vector2Table record [Vector2 memory vector = Vector2Table.get(key)]: 6391 +(src/test/World.t.sol) | call autonomous system via World contract [WorldWithTestSystem(address(world)).TestSystem_move(entity, 1, 2)]: 41998 +(src/test/World.t.sol) | call delegate system via World contract [WorldWithTestSystem(address(world)).TestSystem_move(entity, 1, 2)]: 39940 +(src/test/schemas/AddressArray.t.sol) | set record in AddressArrayTable [AddressArray_.set(tableId, key, addresses)]: 63140 +(src/test/schemas/AddressArray.t.sol) | get record from AddressArrayTable (warm) [address[] memory returnedAddresses = AddressArray_.get(tableId, key)]: 9221 +(src/test/schemas/AddressArray.t.sol) | push record to AddressArrayTable [AddressArray_.push(tableId, key, addresses[0])]: 43794 +(src/test/schemas/CallbackArray.t.sol) | set record in CallbackArrayTable [CallbackArray_.set(tableId, key, callbacks)]: 62926 +(src/test/schemas/CallbackArray.t.sol) | get record from CallbackArrayTable (warm) [bytes24[] memory returnedCallbacks = CallbackArray_.get(tableId, key)]: 8975 +(src/test/schemas/CallbackArray.t.sol) | push record to CallbackArrayTable [CallbackArray_.push(tableId, key, callbacks[0])]: 43790 \ No newline at end of file diff --git a/packages/store/src/tables/Vector2Table.sol b/packages/store/src/tables/Vector2Table.sol index dd4b081b02..91ae6ef4ee 100644 --- a/packages/store/src/tables/Vector2Table.sol +++ b/packages/store/src/tables/Vector2Table.sol @@ -12,7 +12,7 @@ import { Vector2, Vector2_ } from "../schemas/Vector2.sol"; // -- User defined tableId -- -bytes32 constant tableId = keccak256("mud.store.table.vector3"); +bytes32 constant tableId = keccak256("mud.store.table.vector2"); // -- Autogenerated library -- // TODO: autogenerate diff --git a/packages/store/src/test/Gas.t.sol b/packages/store/src/test/Gas.t.sol index fbbefba4bf..cf6455bee1 100644 --- a/packages/store/src/test/Gas.t.sol +++ b/packages/store/src/test/Gas.t.sol @@ -32,21 +32,13 @@ contract GasTest is DSTestPlus { // !gasreport abi decode Mixed memory abiDecoded = abi.decode(abiEncoded, (Mixed)); - // !gasreport abi encode packed - bytes memory abiEncodedPacked = abi.encodePacked(mixed.u32, mixed.u128, mixed.a32, mixed.s); - // !gasreport custom encode bytes memory customEncoded = customEncode(mixed); // !gasreport custom decode Mixed memory customDecoded = customDecode(customEncoded); - console.log( - "Length comparison: abi encode %s, abi encode packed %s, custom %s", - abiEncoded.length, - abiEncodedPacked.length, - customEncoded.length - ); + console.log("Length comparison: abi encode %s, custom %s", abiEncoded.length, customEncoded.length); // !gasreport pass abi encoded bytes to external contract someContract.doSomethingWithBytes(abiEncoded); diff --git a/packages/store/src/test/MixedTable.t.sol b/packages/store/src/test/MixedTable.t.sol index 033274a615..159d35292e 100644 --- a/packages/store/src/test/MixedTable.t.sol +++ b/packages/store/src/test/MixedTable.t.sol @@ -13,10 +13,8 @@ contract MixedTableTest is DSTestPlus, StoreView { Mixed private testMixed; function testRegisterAndGetSchema() public { - uint256 gas = gasleft(); + // !gasreport register MixedTable schema MixedTable.registerSchema(); - gas = gas - gasleft(); - console.log("gas used: %s", gas); Schema registeredSchema = StoreCore.getSchema(MixedTableId); Schema declaredSchema = MixedTable.getSchema(); @@ -33,20 +31,11 @@ contract MixedTableTest is DSTestPlus, StoreView { a32[1] = 4; string memory s = "some string"; - uint256 gas = gasleft(); + // !gasreport set record in MixedTable MixedTable.set({ key: key, u32: 1, u128: 2, a32: a32, s: s }); - gas = gas - gasleft(); - console.log("gas used (set, StoreCore): %s", gas); - gas = gasleft(); + // !gasreport get record from MixedTable Mixed memory mixed = MixedTable.get(key); - gas = gas - gasleft(); - console.log("gas used (get, warm): %s", gas); - - gas = gasleft(); - testMixed = mixed; - gas = gas - gasleft(); - console.log("gas used (set, native solidity): %s", gas); assertEq(mixed.u32, 1); assertEq(mixed.u128, 2); @@ -54,4 +43,13 @@ contract MixedTableTest is DSTestPlus, StoreView { assertEq(mixed.a32[1], 4); assertEq(mixed.s, s); } + + function testCompareSolidity() public { + Mixed memory mixed = Mixed({ u32: 1, u128: 2, a32: new uint32[](2), s: "some string" }); + mixed.a32[0] = 3; + mixed.a32[1] = 4; + + // !gasreport store Mixed struct in storage (native solidity) + testMixed = mixed; + } } diff --git a/packages/store/src/test/PackedCounter.t.sol b/packages/store/src/test/PackedCounter.t.sol index b090830965..5bd4acd580 100644 --- a/packages/store/src/test/PackedCounter.t.sol +++ b/packages/store/src/test/PackedCounter.t.sol @@ -13,16 +13,11 @@ contract PackedCounterTest is DSTestPlus { counters[2] = 3; counters[3] = 4; - uint256 gas = gasleft(); + // !gasreport pack uint16 array into PackedCounter PackedCounter packedCounter = PackedCounter_.pack(counters); - gas = gas - gasleft(); - console.log("gas used (pack): %s", gas); - - gas = gasleft(); + // !gasreport get total of PackedCounter packedCounter.total(); - gas = gas - gasleft(); - console.log("gas used (total): %s", gas); assertEq(packedCounter.total(), 10); } @@ -34,16 +29,10 @@ contract PackedCounterTest is DSTestPlus { counters[2] = 3; counters[3] = 4; - uint256 gas = gasleft(); PackedCounter packedCounter = PackedCounter_.pack(counters); - gas = gas - gasleft(); - console.log("gas used (pack): %s", gas); - - gas = gasleft(); + // !gasreport get value at index of PackedCounter packedCounter.atIndex(3); - gas = gas - gasleft(); - console.log("gas used (at index): %s", gas); assertEq(packedCounter.atIndex(0), 1); assertEq(packedCounter.atIndex(1), 2); @@ -60,10 +49,8 @@ contract PackedCounterTest is DSTestPlus { PackedCounter packedCounter = PackedCounter_.pack(counters); - uint256 gas = gasleft(); + // !gasreport set value at index of PackedCounter packedCounter = packedCounter.setAtIndex(2, 5); - gas = gas - gasleft(); - console.log("gas used (at index): %s", gas); assertEq(packedCounter.atIndex(0), 1); assertEq(packedCounter.atIndex(1), 2); diff --git a/packages/store/src/test/RouteTable.t.sol b/packages/store/src/test/RouteTable.t.sol index 8ef09b9491..e2674f0a3b 100644 --- a/packages/store/src/test/RouteTable.t.sol +++ b/packages/store/src/test/RouteTable.t.sol @@ -11,10 +11,8 @@ import { Schema } from "../Schema.sol"; contract RouteTableTest is DSTestPlus, StoreView { function testRegisterAndGetSchema() public { - uint256 gas = gasleft(); + // !gasreport register RouteTable schema RouteTable.registerSchema(); - gas = gas - gasleft(); - console.log("gas used: %s", gas); Schema registeredSchema = StoreCore.getSchema(RouteTableId); Schema declaredSchema = RouteTable.getSchema(); @@ -30,15 +28,11 @@ contract RouteTableTest is DSTestPlus, StoreView { bytes4 selector = bytes4(0x12345678); uint8 executionMode = 1; - uint256 gas = gasleft(); + // !gasreport set RouteTable record RouteTable.set(key, addr, selector, executionMode); - gas = gas - gasleft(); - console.log("gas used (set): %s", gas); - gas = gasleft(); + // !gasreport get RouteTable record Route memory systemEntry = RouteTable.get(key); - gas = gas - gasleft(); - console.log("gas used (get, warm): %s", gas); assertEq(systemEntry.addr, addr); assertEq(systemEntry.selector, selector); diff --git a/packages/store/src/test/Schema.t.sol b/packages/store/src/test/Schema.t.sol index 49f205539e..2e4625a011 100644 --- a/packages/store/src/test/Schema.t.sol +++ b/packages/store/src/test/Schema.t.sol @@ -18,12 +18,10 @@ contract SchemaTest is DSTestPlus { SchemaType.Uint32Array // 0 bytes (because it's dynamic) ); gas = gas - gasleft(); - console.log("gas used (encode): %s", gas); + console.log("GAS REPORT: encode schema with 6 entries [Schema_.encode]: %s", gas); - gas = gasleft(); + // !gasreport get schema type at index SchemaType schemaType1 = schema.atIndex(0); - gas = gas - gasleft(); - console.log("gas used (decode): %s", gas); assertEq(uint8(schemaType1), uint8(SchemaType.Uint8)); assertEq(uint8(schema.atIndex(1)), uint8(SchemaType.Uint16)); @@ -157,10 +155,8 @@ contract SchemaTest is DSTestPlus { SchemaType.Uint32Array // 0 bytes (because it's dynamic) ); - uint256 gas = gasleft(); + // !gasreport get static data length from schema uint256 length = schema.staticDataLength(); - gas = gas - gasleft(); - console.log("gas used: %s", gas); assertEq(length, 55); } @@ -175,10 +171,8 @@ contract SchemaTest is DSTestPlus { SchemaType.Uint32Array // 0 bytes (because it's dynamic) ); - uint256 gas = gasleft(); + // !gasreport get number of static fields from schema uint256 num = schema.numStaticFields(); - gas = gas - gasleft(); - console.log("gas used: %s", gas); assertEq(num, 5); } @@ -193,10 +187,8 @@ contract SchemaTest is DSTestPlus { SchemaType.Uint32Array // 0 bytes (because it's dynamic) ); - uint256 gas = gasleft(); + // !gasreport get number of dynamic fields from schema uint256 num = schema.numDynamicFields(); - gas = gas - gasleft(); - console.log("gas used: %s", gas); assertEq(num, 1); } @@ -232,10 +224,31 @@ contract SchemaTest is DSTestPlus { schema[26] = SchemaType.Uint32Array; schema[27] = SchemaType.Uint32Array; Schema encodedSchema = Schema_.encode(schema); + + // !gasreport validate schema encodedSchema.validate(); } function testFailValidate() public view { Schema.wrap(keccak256("some invalid schema")).validate(); } + + function testIsEmptyTrue() public { + SchemaType[] memory schema = new SchemaType[](0); + Schema encodedSchema = Schema_.encode(schema); + + // !gasreport check if schema is empty (empty schema) + bool empty = encodedSchema.isEmpty(); + + assertTrue(empty); + } + + function testIsEmptyFalse() public { + Schema encodedSchema = Schema_.encode(SchemaType.Uint256); + + // !gasreport check if schema is empty (non-empty schema) + bool empty = encodedSchema.isEmpty(); + + assertFalse(empty); + } } diff --git a/packages/store/src/test/Storage.t.sol b/packages/store/src/test/Storage.t.sol index b4290af7e6..2e3443ba89 100644 --- a/packages/store/src/test/Storage.t.sol +++ b/packages/store/src/test/Storage.t.sol @@ -27,21 +27,16 @@ contract StorageTest is DSTestPlus { uint256 storagePointerTwoSlotsAfter = storagePointer + 2; // First write some data to storage at the target slot and two slots after the target slot - uint256 gas = gasleft(); + + // !gasreport write 1 storage slot Storage.write(storagePointer, originalDataFirstSlot); - gas = gas - gasleft(); - console.log("gas used (write, %s slots): %s", Utils.divCeil(originalDataFirstSlot.length, 32), gas); - gas = gasleft(); Storage.write(storagePointerTwoSlotsAfter, originalDataLastSlot); - gas = gas - gasleft(); - console.log("gas used (write, %s slots): %s", Utils.divCeil(originalDataLastSlot.length, 32), gas); // Then set the target slot, partially overwriting the first and third slot, but using safeTrail and offset - gas = gasleft(); + + // !gasreport write 34 bytes over 3 storage slots (with offset and safeTrail)) Storage.write(storagePointer, 31, data1); - gas = gas - gasleft(); - console.log("gas used (write, %s slots): %s", Utils.divCeil(data1.length + 31, 32), gas); // Assert the first slot has the correct value assertEq(Storage.read(storagePointer), bytes32(0x4200000000000000000000000000000000000000000000000000000000006901)); @@ -59,10 +54,10 @@ contract StorageTest is DSTestPlus { ); // Assert we can read the correct partial value from storage - gas = gasleft(); + + // !gasreport read 34 bytes over 3 storage slots (with offset and safeTrail)) bytes memory data = Storage.read(storagePointer, 31, 34); - gas = gas - gasleft(); - console.log("gas used (read, %s slots (warm)): %s", Utils.divCeil(data.length + 31, 32), gas); + assertEq(Bytes.slice1(data, 0), bytes1(0x01)); assertEq(Bytes.slice32(data, 1), bytes32(0x0200000000000000000000000000000000000000000000000000000000000003)); assertEq(Bytes.slice1(data, 33), bytes1(0x04)); diff --git a/packages/store/src/test/StoreCore.t.sol b/packages/store/src/test/StoreCore.t.sol index 27678893fa..bd91ecbf15 100644 --- a/packages/store/src/test/StoreCore.t.sol +++ b/packages/store/src/test/StoreCore.t.sol @@ -56,15 +56,11 @@ contract StoreCoreTest is DSTestPlus, StoreView { Schema schema = Schema_.encode(SchemaType.Uint8, SchemaType.Uint16, SchemaType.Uint8, SchemaType.Uint16); bytes32 table = keccak256("some.table"); - uint256 gas = gasleft(); + // !gasreport StoreCore: register schema StoreCore.registerSchema(table, schema); - gas = gas - gasleft(); - console.log("gas used (register): %s", gas); - gas = gasleft(); + // !gasreport StoreCore: get schema (warm) Schema loadedSchema = StoreCore.getSchema(table); - gas = gas - gasleft(); - console.log("gas used (get schema, warm): %s", gas); assertEq(schema.unwrap(), loadedSchema.unwrap()); } @@ -79,15 +75,11 @@ contract StoreCoreTest is DSTestPlus, StoreView { bytes32 table2 = keccak256("other.table"); StoreCore.registerSchema(table, schema); - uint256 gas = gasleft(); + // !gasreport Check for existence of table (existent) StoreCore.hasTable(table); - gas = gas - gasleft(); - console.log("gas used (has): %s", gas); - gas = gasleft(); + // !gasreport check for existence of table (non-existent) StoreCore.hasTable(table2); - gas = gas - gasleft(); - console.log("gas used (has not): %s", gas); assertTrue(StoreCore.hasTable(table)); assertFalse(StoreCore.hasTable(table2)); @@ -112,10 +104,8 @@ contract StoreCoreTest is DSTestPlus, StoreView { key[0] = bytes32("some key"); // Set dynamic data length of dynamic index 0 - uint256 gas = gasleft(); + // !gasreport set dynamic length of dynamic index 0 StoreCore._setDynamicDataLengthAtIndex(table, key, 0, 10); - gas = gas - gasleft(); - console.log("gas used (set length): %s", gas); PackedCounter encodedLength = StoreCore._loadEncodedDynamicDataLength(table, key); assertEq(encodedLength.atIndex(0), 10); @@ -123,10 +113,8 @@ contract StoreCoreTest is DSTestPlus, StoreView { assertEq(encodedLength.total(), 10); // Set dynamic data length of dynamic index 1 - gas = gasleft(); + // !gasreport set dynamic length of dynamic index 1 StoreCore._setDynamicDataLengthAtIndex(table, key, 1, 99); - gas = gas - gasleft(); - console.log("gas used (set length): %s", gas); encodedLength = StoreCore._loadEncodedDynamicDataLength(table, key); assertEq(encodedLength.atIndex(0), 10); @@ -134,10 +122,8 @@ contract StoreCoreTest is DSTestPlus, StoreView { assertEq(encodedLength.total(), 109); // Reduce dynamic data length of dynamic index 0 again - gas = gasleft(); + // !gasreport reduce dynamic length of dynamic index 0 StoreCore._setDynamicDataLengthAtIndex(table, key, 0, 5); - gas = gas - gasleft(); - console.log("gas used (set length): %s", gas); encodedLength = StoreCore._loadEncodedDynamicDataLength(table, key); assertEq(encodedLength.atIndex(0), 5); @@ -158,16 +144,12 @@ contract StoreCoreTest is DSTestPlus, StoreView { bytes32[] memory key = new bytes32[](1); key[0] = keccak256("some.key"); - uint256 gas = gasleft(); + // !gasreport set static record (1 slot) StoreCore.setStaticData(table, key, data); - gas = gas - gasleft(); - console.log("gas used (set): %s", gas); // Get data - gas = gasleft(); + // !gasreport get static record (1 slot) bytes memory loadedData = StoreCore.getRecord(table, key, schema); - gas = gas - gasleft(); - console.log("gas used (get, warm): %s", gas); assertTrue(Bytes.equals(data, loadedData)); } @@ -184,11 +166,8 @@ contract StoreCoreTest is DSTestPlus, StoreView { bytes32[] memory key = new bytes32[](1); key[0] = keccak256("some.key"); - uint256 gas = gasleft(); // This should fail because the data is not 6 bytes long StoreCore.setStaticData(table, key, data); - gas = gas - gasleft(); - console.log("gas used (set): %s", gas); } function testSetAndGetStaticDataSpanningWords() public { @@ -206,16 +185,12 @@ contract StoreCoreTest is DSTestPlus, StoreView { bytes32[] memory key = new bytes32[](1); key[0] = keccak256("some.key"); - uint256 gas = gasleft(); + // !gasreport set static record (2 slots) StoreCore.setStaticData(table, key, data); - gas = gas - gasleft(); - console.log("gas used (set): %s", gas); // Get data - gas = gasleft(); + // !gasreport get static record (2 slots) bytes memory loadedData = StoreCore.getRecord(table, key, schema); - gas = gas - gasleft(); - console.log("gas used (get, warm): %s", gas); assertTrue(Bytes.equals(data, loadedData)); } @@ -264,17 +239,12 @@ contract StoreCoreTest is DSTestPlus, StoreView { key[0] = bytes32("some.key"); // Set data - uint256 gas = gasleft(); + // !gasreport set complex record with dynamic data (4 slots) StoreCore.setRecord(table, key, data); - gas = gas - gasleft(); - console.log("gas used (store complex struct / StoreCore): %s", gas); - // Get data - gas = gasleft(); + // !gasreport get complex record with dynamic data (4 slots) bytes memory loadedData = StoreCore.getRecord(table, key); - gas = gas - gasleft(); - // console.log("gas used (read using StoreCore): %s", gas); assertEq(loadedData.length, data.length); assertEq(keccak256(loadedData), keccak256(data)); @@ -288,15 +258,11 @@ contract StoreCoreTest is DSTestPlus, StoreView { _testStruct.thirdData[1] = 0x1d1e1f20; _testStruct.thirdData[2] = 0x21222324; - gas = gasleft(); + // !gasreport compare: Set complex record with dynamic data using native solidity testStruct = _testStruct; - gas = gas - gasleft(); - console.log("gas used (store complex struct / Native): %s", gas); - gas = gasleft(); + // !gasreport compare: Set complex record with dynamic data using abi.encode testMapping[1234] = abi.encode(_testStruct); - gas = gas - gasleft(); - console.log("gas used (store complex struct / abi.encode): %s", gas); } function testSetAndGetField() public { @@ -320,20 +286,16 @@ contract StoreCoreTest is DSTestPlus, StoreView { key[0] = bytes32("some.key"); // Set first field - uint256 gas = gasleft(); + // !gasreport set static field (1 slot) StoreCore.setField(table, key, 0, bytes.concat(firstDataBytes)); - gas = gas - gasleft(); - console.log("gas used (set uint128, no offset): %s", gas); //////////////// // Static data //////////////// // Get first field - gas = gasleft(); + // !gasreport get static field (1 slot) bytes memory loadedData = StoreCore.getField(table, key, 0); - gas = gas - gasleft(); - console.log("gas used (get uint128, no offset): %s", gas); // Verify loaded data is correct assertEq(loadedData.length, 16); @@ -345,16 +307,12 @@ contract StoreCoreTest is DSTestPlus, StoreView { // Set second field bytes32 secondDataBytes = keccak256("some data"); - gas = gasleft(); + // !gasreport set static field (overlap 2 slot) StoreCore.setField(table, key, 1, bytes.concat(secondDataBytes)); - gas = gas - gasleft(); - console.log("gas used (set uint256, 128 offset): %s", gas); // Get second field - gas = gasleft(); + // !gasreport get static field (overlap 2 slot) loadedData = StoreCore.getField(table, key, 1); - gas = gas - gasleft(); - console.log("gas used (get uint256, 128 offset): %s", gas); // Verify loaded data is correct assertEq(loadedData.length, 32); @@ -391,16 +349,12 @@ contract StoreCoreTest is DSTestPlus, StoreView { } // Set third field - gas = gasleft(); + // !gasreport set dynamic field (1 slot, first dynamic field) StoreCore.setField(table, key, 2, thirdDataBytes); - gas = gas - gasleft(); - console.log("gas used (set uint32[2]): %s", gas); // Get third field - gas = gasleft(); + // !gasreport get dynamic field (1 slot, first dynamic field) loadedData = StoreCore.getField(table, key, 2); - gas = gas - gasleft(); - console.log("gas used (get uint32[2]): %s", gas); // Verify loaded data is correct assertEq(Cast.toUint32Array(Buffer_.fromBytes(loadedData).toArray(4)).length, 2); @@ -415,16 +369,12 @@ contract StoreCoreTest is DSTestPlus, StoreView { assertEq(bytes32(StoreCore.getField(table, key, 1)), bytes32(secondDataBytes)); // Set fourth field - gas = gasleft(); + // !gasreport set dynamic field (1 slot, second dynamic field) StoreCore.setField(table, key, 3, fourthDataBytes); - gas = gas - gasleft(); - console.log("gas used (set uint32[3]): %s", gas); // Get fourth field - gas = gasleft(); + // !gasreport get dynamic field (1 slot, second dynamic field) loadedData = StoreCore.getField(table, key, 3); - gas = gas - gasleft(); - console.log("gas used (get uint32[3]): %s", gas); // Verify loaded data is correct assertEq(loadedData.length, fourthDataBytes.length); @@ -489,10 +439,8 @@ contract StoreCoreTest is DSTestPlus, StoreView { assertEq(keccak256(loadedData), keccak256(data)); // Delete data - uint256 gas = gasleft(); + // !gasreport delete record (complex data, 3 slots) StoreCore.deleteRecord(table, key); - gas = gas - gasleft(); - console.log("gas used (delete): %s", gas); // Verify data is deleted loadedData = StoreCore.getRecord(table, key); diff --git a/packages/store/src/test/StoreSwitch.t.sol b/packages/store/src/test/StoreSwitch.t.sol index 62ea27971c..bdce6508b4 100644 --- a/packages/store/src/test/StoreSwitch.t.sol +++ b/packages/store/src/test/StoreSwitch.t.sol @@ -28,10 +28,8 @@ contract Store is StoreView { // Mock system to wrap StoreSwitch.isDelegateCall() contract System { function isDelegateCall() public view returns (bool isDelegate) { - uint256 gas = gasleft(); + // !gasreport check if delegatecall isDelegate = StoreSwitch.isDelegateCall(); - gas = gas - gasleft(); - console.log("gas used: %s", gas); } } diff --git a/packages/store/src/test/System.t.sol b/packages/store/src/test/System.t.sol index c2c1a131bd..0d22571426 100644 --- a/packages/store/src/test/System.t.sol +++ b/packages/store/src/test/System.t.sol @@ -8,7 +8,9 @@ import { System } from "../System.sol"; contract TestSystem is System { function msgSender() public pure returns (address) { - return _msgSender(); + // !gasreport extract msg.sender from calldata + address sender = _msgSender(); + return sender; } } diff --git a/packages/store/src/test/Vector2Table.t.sol b/packages/store/src/test/Vector2Table.t.sol index 5cc499939b..24860b646a 100644 --- a/packages/store/src/test/Vector2Table.t.sol +++ b/packages/store/src/test/Vector2Table.t.sol @@ -11,10 +11,8 @@ import { Schema } from "../Schema.sol"; contract Vector2TableTest is DSTestPlus, StoreView { function testRegisterAndGetSchema() public { - uint256 gas = gasleft(); + // !gasreport register Vector2Table schema Vector2Table.registerSchema(); - gas = gas - gasleft(); - console.log("gas used: %s", gas); Schema registeredSchema = StoreCore.getSchema(Vector2Id); Schema declaredSchema = Vector2Table.getSchema(); @@ -26,15 +24,11 @@ contract Vector2TableTest is DSTestPlus, StoreView { Vector2Table.registerSchema(); bytes32 key = keccak256("somekey"); - uint256 gas = gasleft(); + // !gasreport set Vector2Table record Vector2Table.set({ key: key, x: 1, y: 2 }); - gas = gas - gasleft(); - console.log("gas used (set): %s", gas); - gas = gasleft(); + // !gasreport get Vector2Table record Vector2 memory vector = Vector2Table.get(key); - gas = gas - gasleft(); - console.log("gas used (get, warm): %s", gas); assertEq(vector.x, 1); assertEq(vector.y, 2); diff --git a/packages/store/src/test/World.t.sol b/packages/store/src/test/World.t.sol index dc9fa1f249..87fa2f20c6 100644 --- a/packages/store/src/test/World.t.sol +++ b/packages/store/src/test/World.t.sol @@ -81,10 +81,9 @@ contract WorldTest is DSTestPlus { // Call autonomous system's move function via world contract bytes32 entity = keccak256("entity"); - uint256 gas = gasleft(); + + // !gasreport call autonomous system via World contract WorldWithTestSystem(address(world)).TestSystem_move(entity, 1, 2); - gas = gas - gasleft(); - console.log("gas used (autonomous system): ", gas); // Get state from the table (using out-of-system syntax) Vector2 memory vec2 = Vector2Table.get(world, entity); @@ -104,10 +103,9 @@ contract WorldTest is DSTestPlus { // Call delegate system's move function via world contract bytes32 entity = keccak256("entity"); - uint256 gas = gasleft(); + + // !gasreport call delegate system via World contract WorldWithTestSystem(address(world)).TestSystem_move(entity, 1, 2); - gas = gas - gasleft(); - console.log("gas used (delegate system): ", gas); // Get state from the table (using out-of-system syntax) Vector2 memory vec2 = Vector2Table.get(world, entity); From 104f75137a137a1d8352afd10999acace22c554b Mon Sep 17 00:00:00 2001 From: alvrs Date: Wed, 1 Feb 2023 16:56:26 +0100 Subject: [PATCH 36/82] build(store): fix CI, add gas report action --- packages/store/gas-report.txt | 8 +- packages/store/package.json | 3 +- packages/store/src/Storage2.sol | 221 ---------------------- packages/store/src/test/StoreSwitch.t.sol | 4 +- packages/store/src/test/World.t.sol | 38 ++-- 5 files changed, 24 insertions(+), 250 deletions(-) delete mode 100644 packages/store/src/Storage2.sol diff --git a/packages/store/gas-report.txt b/packages/store/gas-report.txt index 887c3f0743..b43caa2949 100644 --- a/packages/store/gas-report.txt +++ b/packages/store/gas-report.txt @@ -115,10 +115,4 @@ (src/test/Vector2Table.t.sol) | set Vector2Table record [Vector2Table.set({ key: key, x: 1, y: 2 })]: 29689 (src/test/Vector2Table.t.sol) | get Vector2Table record [Vector2 memory vector = Vector2Table.get(key)]: 6391 (src/test/World.t.sol) | call autonomous system via World contract [WorldWithTestSystem(address(world)).TestSystem_move(entity, 1, 2)]: 41998 -(src/test/World.t.sol) | call delegate system via World contract [WorldWithTestSystem(address(world)).TestSystem_move(entity, 1, 2)]: 39940 -(src/test/schemas/AddressArray.t.sol) | set record in AddressArrayTable [AddressArray_.set(tableId, key, addresses)]: 63140 -(src/test/schemas/AddressArray.t.sol) | get record from AddressArrayTable (warm) [address[] memory returnedAddresses = AddressArray_.get(tableId, key)]: 9221 -(src/test/schemas/AddressArray.t.sol) | push record to AddressArrayTable [AddressArray_.push(tableId, key, addresses[0])]: 43794 -(src/test/schemas/CallbackArray.t.sol) | set record in CallbackArrayTable [CallbackArray_.set(tableId, key, callbacks)]: 62926 -(src/test/schemas/CallbackArray.t.sol) | get record from CallbackArrayTable (warm) [bytes24[] memory returnedCallbacks = CallbackArray_.get(tableId, key)]: 8975 -(src/test/schemas/CallbackArray.t.sol) | push record to CallbackArrayTable [CallbackArray_.push(tableId, key, callbacks[0])]: 43790 \ No newline at end of file +(src/test/World.t.sol) | call delegate system via World contract [WorldWithTestSystem(address(world)).TestSystem_move(entity, 1, 2)]: 39940 \ No newline at end of file diff --git a/packages/store/package.json b/packages/store/package.json index 340c99b752..9eee7235a5 100644 --- a/packages/store/package.json +++ b/packages/store/package.json @@ -21,7 +21,8 @@ "lint": "yarn prettier && yarn solhint", "link": "yarn link", "docs": "rimraf API && hardhat docgen && echo 'label: API\norder: 50' > API/index.yml", - "release": "npm publish || echo 'version already published'" + "release": "npm publish || echo 'version already published'", + "gasreport": "yarn mud gas-report --path src/test/** --save gas-report.txt" }, "devDependencies": { "@typechain/ethers-v5": "^9.0.0", diff --git a/packages/store/src/Storage2.sol b/packages/store/src/Storage2.sol deleted file mode 100644 index 26ed236811..0000000000 --- a/packages/store/src/Storage2.sol +++ /dev/null @@ -1,221 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.8.0; - -/** - * @author @dk1a - * from https://github.com/latticexyz/mud/blob/c4cae23ac0284d6d475b683ad145daaf748e910e/packages/solecs/v2/storage.sol - */ - -import { Utils } from "./Utils.sol"; - -library Storage { - function write(uint256 slotDest, bytes memory data) internal { - write(slotDest, 0, true, data); - } - - function write( - uint256 slotDest, - uint256 slotByteOffset, - bytes memory data - ) internal { - write(slotDest, slotByteOffset, true, data); - } - - function write( - uint256 slotDest, - uint256 slotByteOffset, - bool safeTail, - bytes memory data - ) internal { - // slots are measured in words, so a byte offset is needed for data within the word - if (slotByteOffset > 0) { - unchecked { - slotDest += slotByteOffset / 32; - slotByteOffset %= 32; - } - } - - uint256 length = data.length; - uint256 ptrSrc; - assembly { - // skip length - ptrSrc := add(data, 0x20) - } - - // copy unaligned prefix if necessary - if (slotByteOffset > 0) { - uint256 mask = Utils.leftMask(length * 8); - // this makes a middle mask that will have 0s on the left and *might* have 0s on the right - mask >>= slotByteOffset * 8; - - /// @solidity memory-safe-assembly - assembly { - sstore( - slotDest, - or( - // store the middle part - and(mload(ptrSrc), mask), - // preserve the surrounding parts - and(sload(slotDest), not(mask)) - ) - ) - } - - uint256 prefixLength; - // safe because of `slotByteOffset %= 32` at the start - unchecked { - prefixLength = 32 - slotByteOffset; - } - // return if done - if (length <= prefixLength) return; - - slotDest += 1; - // safe because of `length <= prefixLength` earlier - unchecked { - ptrSrc += prefixLength; - length -= prefixLength; - } - } - - // copy 32-byte chunks - while (length >= 32) { - /// @solidity memory-safe-assembly - assembly { - sstore(slotDest, mload(ptrSrc)) - } - slotDest += 1; - // safe because total addition will be <= length (ptr+len is implicitly safe) - unchecked { - ptrSrc += 32; - length -= 32; - } - } - - // return if nothing is left - if (length == 0) return; - - // copy the 0-31 length tail - if (safeTail) { - // preserve the trailing bytes after the tail - - uint256 mask = Utils.leftMask(length * 8); - /// @solidity memory-safe-assembly - assembly { - sstore( - slotDest, - or( - // store the left part - and(mload(ptrSrc), mask), - // preserve the right part - and(sload(slotDest), not(mask)) - ) - ) - } - } else { - // overwrite the trailing bytes after the tail with garbage from memory - // (this is fine only at the end of a sparse storage slot) - - /// @solidity memory-safe-assembly - assembly { - sstore(slotDest, mload(ptrSrc)) - } - } - } - - function read(uint256 slotSrc) internal view returns (bytes32 data) { - assembly { - data := sload(slotSrc) - } - } - - function read( - uint256 slotSrc, - uint256 slotByteOffset, - uint256 length - ) internal view returns (bytes memory data) { - // slots are measured in words, so a byte offset is needed for data within the word - if (slotByteOffset > 0) { - unchecked { - slotSrc += slotByteOffset / 32; - slotByteOffset %= 32; - } - } - - data = new bytes(length); - - uint256 ptrDest; - assembly { - // skip length - ptrDest := add(data, 0x20) - } - - uint256 mask; - // copy unaligned prefix if necessary - if (slotByteOffset > 0) { - mask = Utils.leftMask(length * 8); - // this makes a middle mask that will have 0s on the left and *might* have 0s on the right - mask >>= slotByteOffset * 8; - - /// @solidity memory-safe-assembly - assembly { - mstore( - ptrDest, - or( - // store the middle part - and(sload(slotSrc), mask), - // preserve the surrounding parts - and(mload(ptrDest), not(mask)) - ) - ) - } - - uint256 prefixLength; - // safe because of `slotByteOffset %= 32` revert at the start - unchecked { - prefixLength = 32 - slotByteOffset; - } - // return if done - if (length <= prefixLength) return data; - - slotSrc += 1; - // safe because of `length <= prefixLength` earlier - unchecked { - ptrDest += prefixLength; - length -= prefixLength; - } - } - - // copy 32-byte chunks - while (length >= 32) { - /// @solidity memory-safe-assembly - assembly { - mstore(ptrDest, sload(slotSrc)) - } - slotSrc += 1; - // safe because total addition will be <= length (ptr+len is implicitly safe) - unchecked { - ptrDest += 32; - length -= 32; - } - } - - // return if nothing is left - if (length == 0) return data; - - // copy the 0-31 length tail - // (always preserve the trailing bytes after the tail, an extra mload is cheap) - mask = Utils.leftMask(length * 8); - /// @solidity memory-safe-assembly - assembly { - mstore( - ptrDest, - or( - // store the left part - and(sload(slotSrc), mask), - // preserve the right part - and(mload(ptrDest), not(mask)) - ) - ) - } - } -} diff --git a/packages/store/src/test/StoreSwitch.t.sol b/packages/store/src/test/StoreSwitch.t.sol index bdce6508b4..81e99643cf 100644 --- a/packages/store/src/test/StoreSwitch.t.sol +++ b/packages/store/src/test/StoreSwitch.t.sol @@ -10,7 +10,7 @@ import { StoreSwitch } from "../StoreSwitch.sol"; // Mock Store to call MockSystem contract Store is StoreView { - System mockSystem = new System(); + MockSystem mockSystem = new MockSystem(); function callViaDelegateCall() public returns (bool isDelegate) { (bool success, bytes memory data) = address(mockSystem).delegatecall(abi.encodeWithSignature("isDelegateCall()")); @@ -26,7 +26,7 @@ contract Store is StoreView { } // Mock system to wrap StoreSwitch.isDelegateCall() -contract System { +contract MockSystem { function isDelegateCall() public view returns (bool isDelegate) { // !gasreport check if delegatecall isDelegate = StoreSwitch.isDelegateCall(); diff --git a/packages/store/src/test/World.t.sol b/packages/store/src/test/World.t.sol index 87fa2f20c6..4a54e487d8 100644 --- a/packages/store/src/test/World.t.sol +++ b/packages/store/src/test/World.t.sol @@ -8,12 +8,12 @@ import { System } from "../System.sol"; import { ExecutionMode } from "../Types.sol"; import { Vector2Table, Vector2 } from "../tables/Vector2Table.sol"; -contract TestSystem is System { +contract WorldTestSystem is System { function msgSender() public pure returns (address) { return _msgSender(); } - // TestSystem's move function sets state on the Vector2Table of the caller + // WorldTestSystem's move function sets state on the Vector2Table of the caller // (independent of delegatecall or regular call) function move( bytes32 entity, @@ -25,10 +25,10 @@ contract TestSystem is System { } // TODO: auto-generate this interface from registered systems -interface WorldWithTestSystem { - function TestSystem_msgSender() external view returns (address); +interface WorldWithWorldTestSystem { + function WorldTestSystem_msgSender() external view returns (address); - function TestSystem_move( + function WorldTestSystem_move( bytes32 entity, uint32 x, uint32 y @@ -37,19 +37,19 @@ interface WorldWithTestSystem { contract WorldTest is DSTestPlus { World internal world; - TestSystem internal system; + WorldTestSystem internal system; function setUp() public { world = new World(); - system = new TestSystem(); + system = new WorldTestSystem(); } function testRegisterAndCallSystem() public { // Register autonomous system in the world - world.registerSystem(address(system), "TestSystem", "msgSender()", ExecutionMode.Autonomous); + world.registerSystem(address(system), "WorldTestSystem", "msgSender()", ExecutionMode.Autonomous); // Call system via world contract - address msgSender = WorldWithTestSystem(address(world)).TestSystem_msgSender(); + address msgSender = WorldWithWorldTestSystem(address(world)).WorldTestSystem_msgSender(); // msg.sender (this) should have been passed to system assertEq(msgSender, address(this)); @@ -57,10 +57,10 @@ contract WorldTest is DSTestPlus { function testRegisterAndDelegatecallSystem() public { // Register delegate system in the world - world.registerSystem(address(system), "TestSystem", "msgSender()", ExecutionMode.Delegate); + world.registerSystem(address(system), "WorldTestSystem", "msgSender()", ExecutionMode.Delegate); // Call system via world contract - address msgSender = WorldWithTestSystem(address(world)).TestSystem_msgSender(); + address msgSender = WorldWithWorldTestSystem(address(world)).WorldTestSystem_msgSender(); // msg.sender (this) should have been passed to system assertEq(msgSender, address(this)); @@ -71,10 +71,10 @@ contract WorldTest is DSTestPlus { Vector2Table.registerSchema(world); } - // Register TestSystem as autonomous system and call its move function to set state on Vector2Table - function testSetVector2ViaTestSystemAutonomous() public { + // Register WorldTestSystem as autonomous system and call its move function to set state on Vector2Table + function testSetVector2ViaWorldTestSystemAutonomous() public { // Register autonomous system in the world - world.registerSystem(address(system), "TestSystem", "move(bytes32,uint32,uint32)", ExecutionMode.Autonomous); + world.registerSystem(address(system), "WorldTestSystem", "move(bytes32,uint32,uint32)", ExecutionMode.Autonomous); // Register table Vector2Table.registerSchema(world); @@ -83,7 +83,7 @@ contract WorldTest is DSTestPlus { bytes32 entity = keccak256("entity"); // !gasreport call autonomous system via World contract - WorldWithTestSystem(address(world)).TestSystem_move(entity, 1, 2); + WorldWithWorldTestSystem(address(world)).WorldTestSystem_move(entity, 1, 2); // Get state from the table (using out-of-system syntax) Vector2 memory vec2 = Vector2Table.get(world, entity); @@ -93,10 +93,10 @@ contract WorldTest is DSTestPlus { assertEq(vec2.y, 2); } - // Register TestSystem as delegate system and call its move function to set state on Vector2Table - function testSetVector2ViaTestSystemDelegate() public { + // Register WorldTestSystem as delegate system and call its move function to set state on Vector2Table + function testSetVector2ViaWorldTestSystemDelegate() public { // Register delegate system in the world - world.registerSystem(address(system), "TestSystem", "move(bytes32,uint32,uint32)", ExecutionMode.Delegate); + world.registerSystem(address(system), "WorldTestSystem", "move(bytes32,uint32,uint32)", ExecutionMode.Delegate); // Register table Vector2Table.registerSchema(world); @@ -105,7 +105,7 @@ contract WorldTest is DSTestPlus { bytes32 entity = keccak256("entity"); // !gasreport call delegate system via World contract - WorldWithTestSystem(address(world)).TestSystem_move(entity, 1, 2); + WorldWithWorldTestSystem(address(world)).WorldTestSystem_move(entity, 1, 2); // Get state from the table (using out-of-system syntax) Vector2 memory vec2 = Vector2Table.get(world, entity); From 77f07abb495c9f9060fe2aa112ddc50833095727 Mon Sep 17 00:00:00 2001 From: alvrs Date: Wed, 1 Feb 2023 17:00:44 +0100 Subject: [PATCH 37/82] build(store): update gas report action name --- .github/workflows/gasreport.yml | 42 +++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 .github/workflows/gasreport.yml diff --git a/.github/workflows/gasreport.yml b/.github/workflows/gasreport.yml new file mode 100644 index 0000000000..12eb13ae8c --- /dev/null +++ b/.github/workflows/gasreport.yml @@ -0,0 +1,42 @@ +# .github/workflows/gasreport.yml +name: Store gas report + +on: + pull_request: + paths: + - packages/store/** + +jobs: + test: + runs-on: ubuntu-22.04 + name: Run tests + steps: + - uses: actions/setup-node@v3 + with: + node-version: 16 + + - name: git-checkout + uses: actions/checkout@v2 + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + with: + version: nightly + + - name: Install dependencies + run: yarn install --network-concurrency 1 + + - name: Run gas report + run: yarn workspace @latticexyz/store run gasreport + + - name: Commit updated workflow + run: | + git config --local user.email "github-actions@github.com" + git config --local user.name "github-actions" + git commit -a -m "chore(store): update gas report" + + - name: Push changes + uses: ad-m/github-push-action@master + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + branch: ${{ github.ref }} From f4278efc8c0b3d99ffa59ac309733bd9515dc414 Mon Sep 17 00:00:00 2001 From: alvrs Date: Wed, 1 Feb 2023 17:08:03 +0100 Subject: [PATCH 38/82] refactor(store): use setRecord instead of setStaticData --- packages/store/src/IStore.sol | 7 ---- packages/store/src/StoreCore.sol | 47 ------------------------- packages/store/src/StoreSwitch.sol | 12 ------- packages/store/src/StoreView.sol | 8 ----- packages/store/src/World.sol | 8 ----- packages/store/src/schemas/Route.sol | 2 +- packages/store/src/schemas/Vector2.sol | 2 +- packages/store/src/test/StoreCore.t.sol | 6 ++-- 8 files changed, 5 insertions(+), 87 deletions(-) diff --git a/packages/store/src/IStore.sol b/packages/store/src/IStore.sol index 6102db890e..aef0f7d951 100644 --- a/packages/store/src/IStore.sol +++ b/packages/store/src/IStore.sol @@ -20,13 +20,6 @@ interface IStore { bytes memory data ) external; - // TODO: remove this function, use setRecord instead - function setStaticData( - bytes32 table, - bytes32[] memory key, - bytes memory data - ) external; - // Set partial data at schema index function setField( bytes32 table, diff --git a/packages/store/src/StoreCore.sol b/packages/store/src/StoreCore.sol index 5a6f064a54..b3dfbbe500 100644 --- a/packages/store/src/StoreCore.sol +++ b/packages/store/src/StoreCore.sol @@ -173,32 +173,6 @@ library StoreCore { emit StoreSetRecord(table, key, data); } - /** - * Set full static data record for the given table and key tuple (without any dynamic data) - * TODO: remove this function and just use setRecord - */ - function setStaticData( - bytes32 table, - bytes32[] memory key, - bytes memory data - ) internal { - // verify the value has the correct length for the table (based on the table's schema) - // to prevent invalid data from being stored - Schema schema = getSchema(table); - - // Verify the static data length matches the table schema - if (schema.staticDataLength() != data.length) { - revert StoreCore_InvalidDataLength(schema.staticDataLength(), data.length); - } - - // Store the provided value in storage - bytes32 location = _getStaticDataLocation(table, key); - Storage.write(location, data); - - // Emit event to notify indexers - emit StoreSetRecord(table, key, data); - } - function setField( bytes32 table, bytes32[] memory key, @@ -523,27 +497,6 @@ library StoreCoreExt { * ************************************************************************/ - function setStaticData( - bytes32 table, - bytes32 _key, - bytes memory data - ) internal { - bytes32[] memory key = new bytes32[](1); - key[0] = _key; - StoreCore.setStaticData(table, key, data); - } - - function setStaticData( - bytes32 table, - bytes32[2] memory _key, - bytes memory data - ) internal { - bytes32[] memory key = new bytes32[](2); - key[0] = _key[0]; - key[1] = _key[1]; - StoreCore.setStaticData(table, key, data); - } - /************************************************************************ * * GET DATA diff --git a/packages/store/src/StoreSwitch.sol b/packages/store/src/StoreSwitch.sol index d9a239a33b..d774ce4eee 100644 --- a/packages/store/src/StoreSwitch.sol +++ b/packages/store/src/StoreSwitch.sol @@ -61,18 +61,6 @@ library StoreSwitch { } } - function setStaticData( - bytes32 table, - bytes32[] memory key, - bytes memory data - ) internal { - if (isDelegateCall()) { - StoreCore.setStaticData(table, key, data); - } else { - IStore(msg.sender).setStaticData(table, key, data); - } - } - function setField( bytes32 table, bytes32[] memory key, diff --git a/packages/store/src/StoreView.sol b/packages/store/src/StoreView.sol index cccb468812..1591d12e58 100644 --- a/packages/store/src/StoreView.sol +++ b/packages/store/src/StoreView.sol @@ -30,14 +30,6 @@ contract StoreView is IStore { revert Store_BaseContractNotImplemented(); } - function setStaticData( - bytes32, - bytes32[] memory, - bytes memory - ) public virtual { - revert Store_BaseContractNotImplemented(); - } - // Set partial data at schema index function setField( bytes32, diff --git a/packages/store/src/World.sol b/packages/store/src/World.sol index 032252e9b5..0ef4a14604 100644 --- a/packages/store/src/World.sol +++ b/packages/store/src/World.sol @@ -31,14 +31,6 @@ contract World is StoreView { StoreCore.setRecord(table, key, data); } - function setStaticData( - bytes32 table, - bytes32[] memory key, - bytes memory data - ) public override { - StoreCore.setStaticData(table, key, data); - } - function setField( bytes32 table, bytes32[] memory key, diff --git a/packages/store/src/schemas/Route.sol b/packages/store/src/schemas/Route.sol index 2e96da7339..d33c75ae72 100644 --- a/packages/store/src/schemas/Route.sol +++ b/packages/store/src/schemas/Route.sol @@ -46,7 +46,7 @@ library Route_ { bytes memory data = bytes.concat(bytes20(addr), bytes4(selector), bytes1(executionMode)); bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = key; - StoreSwitch.setStaticData(tableId, keyTuple, data); + StoreSwitch.setRecord(tableId, keyTuple, data); } function set( diff --git a/packages/store/src/schemas/Vector2.sol b/packages/store/src/schemas/Vector2.sol index 00349fd407..8f8d9b2a39 100644 --- a/packages/store/src/schemas/Vector2.sol +++ b/packages/store/src/schemas/Vector2.sol @@ -44,7 +44,7 @@ library Vector2_ { bytes memory data = bytes.concat(bytes4(x), bytes4(y)); bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = key; - StoreSwitch.setStaticData(tableId, keyTuple, data); + StoreSwitch.setRecord(tableId, keyTuple, data); } function set( diff --git a/packages/store/src/test/StoreCore.t.sol b/packages/store/src/test/StoreCore.t.sol index bd91ecbf15..7b61ea45a1 100644 --- a/packages/store/src/test/StoreCore.t.sol +++ b/packages/store/src/test/StoreCore.t.sol @@ -145,7 +145,7 @@ contract StoreCoreTest is DSTestPlus, StoreView { key[0] = keccak256("some.key"); // !gasreport set static record (1 slot) - StoreCore.setStaticData(table, key, data); + StoreCore.setRecord(table, key, data); // Get data // !gasreport get static record (1 slot) @@ -167,7 +167,7 @@ contract StoreCoreTest is DSTestPlus, StoreView { key[0] = keccak256("some.key"); // This should fail because the data is not 6 bytes long - StoreCore.setStaticData(table, key, data); + StoreCore.setRecord(table, key, data); } function testSetAndGetStaticDataSpanningWords() public { @@ -186,7 +186,7 @@ contract StoreCoreTest is DSTestPlus, StoreView { key[0] = keccak256("some.key"); // !gasreport set static record (2 slots) - StoreCore.setStaticData(table, key, data); + StoreCore.setRecord(table, key, data); // Get data // !gasreport get static record (2 slots) From b1baf0839eb67b70fca114f9b24554ec788b7306 Mon Sep 17 00:00:00 2001 From: alvrs Date: Wed, 1 Feb 2023 17:09:39 +0100 Subject: [PATCH 39/82] build(store): add mud cli as dev dependency --- packages/store/package.json | 5 ++- yarn.lock | 62 +++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/packages/store/package.json b/packages/store/package.json index 9eee7235a5..895e705b0b 100644 --- a/packages/store/package.json +++ b/packages/store/package.json @@ -43,5 +43,8 @@ "ds-test": "https://github.com/dapphub/ds-test.git#c7a36fb236f298e04edf28e2fee385b80f53945f", "forge-std": "https://github.com/foundry-rs/forge-std.git#37a3fe48c3a4d8239cda93445f0b5e76b1507436" }, - "gitHead": "218f56893d268b0c5157a3e4c603b859e287a343" + "gitHead": "218f56893d268b0c5157a3e4c603b859e287a343", + "dependencies": { + "@latticexyz/cli": "^1.34.0" + } } diff --git a/yarn.lock b/yarn.lock index cbf2439125..e401de3a41 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1703,6 +1703,63 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" +"@latticexyz/cli@^1.34.0": + version "1.34.0" + resolved "https://registry.yarnpkg.com/@latticexyz/cli/-/cli-1.34.0.tgz#df43fe98723117d4b0bcc13bfe9b8e2740e05dcc" + integrity sha512-lZt4nkUuAmR7Q2y5RvFR+vYGAJG+U4iKPrvhD/9pXNmsl+wbR06Pe3+aGcuMWjviV04gY6xdER95D3F95O5uiQ== + dependencies: + "@latticexyz/services" "^1.34.0" + "@latticexyz/solecs" "^1.34.0" + "@latticexyz/std-contracts" "^1.34.0" + "@latticexyz/utils" "^1.34.0" + "@typechain/ethers-v5" "^10.1.1" + chalk "^5.0.1" + chokidar "^3.5.3" + clear "^0.1.0" + commander "^9.2.0" + ds-test "https://github.com/dapphub/ds-test.git#c9ce3f25bde29fc5eb9901842bf02850dfd2d084" + ejs "^3.1.8" + esm "^3.2.25" + ethers "^5.7.2" + execa "^6.1.0" + figlet "^1.5.2" + forge-std "https://github.com/foundry-rs/forge-std.git#f36dab24d63d1c1945a05ed375ce341d3c1a49ed" + glob "^8.0.3" + inquirer "^8.2.4" + inquirer-prompt-suggest "^0.1.0" + listr2 "^4.0.5" + netlify "^11.0.1" + nice-grpc-web "^2.0.1" + node-fetch "^3.2.6" + openurl "^1.1.1" + path "^0.12.7" + solmate "https://github.com/Rari-Capital/solmate.git#9cf1428245074e39090dceacb0c28b1f684f584c" + typechain "^8.1.1" + uuid "^8.3.2" + yargs "^17.5.1" + +"@latticexyz/services@^1.34.0": + version "1.34.0" + resolved "https://registry.yarnpkg.com/@latticexyz/services/-/services-1.34.0.tgz#3085d391c54f845a7a17f78ba874592f5651d80d" + integrity sha512-0oqk9ys1JxnlKIbzp+zsdQ59p0CLWm4zRZsx83GjJKb/9XiiCo26/GDrV00ALHAEtRU4dCrP9mbmaY8zrCOc5w== + +"@latticexyz/solecs@^1.34.0": + version "1.34.0" + resolved "https://registry.yarnpkg.com/@latticexyz/solecs/-/solecs-1.34.0.tgz#bfbf6ca5b577f5c5f5912a35704d99c1ecc089e9" + integrity sha512-tgUyHJWjs03PeOiqxzc+P98NUv2GcH+Ml87puhb/7RFv8WLrus7QOjkiwUcHHRO6+UZCBwr34ySaCY3AzJ4nUg== + +"@latticexyz/std-contracts@^1.34.0": + version "1.34.0" + resolved "https://registry.yarnpkg.com/@latticexyz/std-contracts/-/std-contracts-1.34.0.tgz#3f2bdae721d7aaa641f94ca83592592ab60055f8" + integrity sha512-eLX6qLQV+wQoJpkcO9OFFYB/uJq1ih+hZ1Ufg1n5NzOGzf3kWqJTwQwtSaYWDwUe9bszw2FtreivyqmwfYwubw== + +"@latticexyz/utils@^1.34.0": + version "1.34.0" + resolved "https://registry.yarnpkg.com/@latticexyz/utils/-/utils-1.34.0.tgz#90d98ac11eb3da8e9aa81af686f0f41151119d74" + integrity sha512-tr/LV4QfhV0XYjtrY8DHLYEI2EPHzVQXmK1JtjupstLvK31WpE9xvgIqMYTawxupC9xw2c5q6zxEs/JZtiUmSQ== + dependencies: + typedoc-plugin-markdown "^3.13.6" + "@lerna/add@4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@lerna/add/-/add-4.0.0.tgz#c36f57d132502a57b9e7058d1548b7a565ef183f" @@ -13863,6 +13920,11 @@ solidity-docgen@^0.6.0-beta.22: handlebars "^4.7.7" solidity-ast "^0.4.31" +"solmate@git+https://github.com/Rari-Capital/solmate.git#9cf1428245074e39090dceacb0c28b1f684f584c": + version "6.5.0" + uid "9cf1428245074e39090dceacb0c28b1f684f584c" + resolved "git+https://github.com/Rari-Capital/solmate.git#9cf1428245074e39090dceacb0c28b1f684f584c" + "solmate@https://github.com/Rari-Capital/solmate.git#9cf1428245074e39090dceacb0c28b1f684f584c": version "6.5.0" resolved "https://github.com/Rari-Capital/solmate.git#9cf1428245074e39090dceacb0c28b1f684f584c" From dbde0dca1f19cc8f3e978336e2b13d289324a8cd Mon Sep 17 00:00:00 2001 From: alvrs Date: Wed, 1 Feb 2023 17:14:56 +0100 Subject: [PATCH 40/82] build(store): update forge-std and ds-test dependencies --- packages/store/package.json | 8 ++++---- yarn.lock | 5 ----- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/packages/store/package.json b/packages/store/package.json index 895e705b0b..2391bcf54d 100644 --- a/packages/store/package.json +++ b/packages/store/package.json @@ -27,8 +27,8 @@ "devDependencies": { "@typechain/ethers-v5": "^9.0.0", "@types/mocha": "^9.1.1", - "ds-test": "https://github.com/dapphub/ds-test.git#c7a36fb236f298e04edf28e2fee385b80f53945f", - "forge-std": "https://github.com/foundry-rs/forge-std.git#37a3fe48c3a4d8239cda93445f0b5e76b1507436", + "ds-test": "https://github.com/dapphub/ds-test.git#c9ce3f25bde29fc5eb9901842bf02850dfd2d084", + "forge-std": "https://github.com/foundry-rs/forge-std.git#f36dab24d63d1c1945a05ed375ce341d3c1a49ed", "hardhat": "^2.10.1", "prettier": "^2.6.2", "prettier-plugin-solidity": "^1.0.0-beta.19", @@ -40,8 +40,8 @@ "typedoc": "^0.23.10" }, "peerDependencies": { - "ds-test": "https://github.com/dapphub/ds-test.git#c7a36fb236f298e04edf28e2fee385b80f53945f", - "forge-std": "https://github.com/foundry-rs/forge-std.git#37a3fe48c3a4d8239cda93445f0b5e76b1507436" + "ds-test": "https://github.com/dapphub/ds-test.git#c9ce3f25bde29fc5eb9901842bf02850dfd2d084", + "forge-std": "https://github.com/foundry-rs/forge-std.git#f36dab24d63d1c1945a05ed375ce341d3c1a49ed" }, "gitHead": "218f56893d268b0c5157a3e4c603b859e287a343", "dependencies": { diff --git a/yarn.lock b/yarn.lock index e401de3a41..49db7ada7c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13920,11 +13920,6 @@ solidity-docgen@^0.6.0-beta.22: handlebars "^4.7.7" solidity-ast "^0.4.31" -"solmate@git+https://github.com/Rari-Capital/solmate.git#9cf1428245074e39090dceacb0c28b1f684f584c": - version "6.5.0" - uid "9cf1428245074e39090dceacb0c28b1f684f584c" - resolved "git+https://github.com/Rari-Capital/solmate.git#9cf1428245074e39090dceacb0c28b1f684f584c" - "solmate@https://github.com/Rari-Capital/solmate.git#9cf1428245074e39090dceacb0c28b1f684f584c": version "6.5.0" resolved "https://github.com/Rari-Capital/solmate.git#9cf1428245074e39090dceacb0c28b1f684f584c" From 9db9ea08a4dbf3c662725373af62b8d2ef9bf47c Mon Sep 17 00:00:00 2001 From: alvrs Date: Wed, 1 Feb 2023 17:35:39 +0100 Subject: [PATCH 41/82] chore(store): use forge-std Test, remove ds-test dependency, fix gas-report command --- packages/store/package.json | 12 ++++-------- packages/store/remappings.txt | 1 - packages/store/src/test/Buffer.t.sol | 5 ++--- packages/store/src/test/Bytes.t.sol | 5 ++--- packages/store/src/test/Gas.t.sol | 5 ++--- packages/store/src/test/MixedTable.t.sol | 5 ++--- packages/store/src/test/PackedCounter.t.sol | 5 ++--- packages/store/src/test/RouteTable.t.sol | 5 ++--- packages/store/src/test/Schema.t.sol | 5 ++--- packages/store/src/test/Storage.t.sol | 5 ++--- packages/store/src/test/StoreCore.t.sol | 5 ++--- packages/store/src/test/StoreSwitch.t.sol | 5 ++--- packages/store/src/test/System.t.sol | 5 ++--- packages/store/src/test/Vector2Table.t.sol | 5 ++--- packages/store/src/test/World.t.sol | 5 ++--- packages/store/src/test/schemas/AddressArray.t.sol | 5 ++--- packages/store/src/test/schemas/CallbackArray.t.sol | 5 ++--- 17 files changed, 34 insertions(+), 54 deletions(-) diff --git a/packages/store/package.json b/packages/store/package.json index 2391bcf54d..59dd94fd05 100644 --- a/packages/store/package.json +++ b/packages/store/package.json @@ -22,12 +22,11 @@ "link": "yarn link", "docs": "rimraf API && hardhat docgen && echo 'label: API\norder: 50' > API/index.yml", "release": "npm publish || echo 'version already published'", - "gasreport": "yarn mud gas-report --path src/test/** --save gas-report.txt" + "gasreport": "yarn @latticexyz/cli gas-report --path src/test/** --save gas-report.txt" }, "devDependencies": { "@typechain/ethers-v5": "^9.0.0", "@types/mocha": "^9.1.1", - "ds-test": "https://github.com/dapphub/ds-test.git#c9ce3f25bde29fc5eb9901842bf02850dfd2d084", "forge-std": "https://github.com/foundry-rs/forge-std.git#f36dab24d63d1c1945a05ed375ce341d3c1a49ed", "hardhat": "^2.10.1", "prettier": "^2.6.2", @@ -37,14 +36,11 @@ "solidity-docgen": "^0.6.0-beta.22", "ts-node": "10.7", "typechain": "^8.1.1", - "typedoc": "^0.23.10" + "typedoc": "^0.23.10", + "@latticexyz/cli": "^1.34.0" }, "peerDependencies": { - "ds-test": "https://github.com/dapphub/ds-test.git#c9ce3f25bde29fc5eb9901842bf02850dfd2d084", "forge-std": "https://github.com/foundry-rs/forge-std.git#f36dab24d63d1c1945a05ed375ce341d3c1a49ed" }, - "gitHead": "218f56893d268b0c5157a3e4c603b859e287a343", - "dependencies": { - "@latticexyz/cli": "^1.34.0" - } + "gitHead": "218f56893d268b0c5157a3e4c603b859e287a343" } diff --git a/packages/store/remappings.txt b/packages/store/remappings.txt index aeeced7eb6..f4a3a9687a 100644 --- a/packages/store/remappings.txt +++ b/packages/store/remappings.txt @@ -1,2 +1 @@ -ds-test/=../../node_modules/ds-test/src/ forge-std/=../../node_modules/forge-std/src/ diff --git a/packages/store/src/test/Buffer.t.sol b/packages/store/src/test/Buffer.t.sol index 0da3aa7bdc..b2a7c92449 100644 --- a/packages/store/src/test/Buffer.t.sol +++ b/packages/store/src/test/Buffer.t.sol @@ -1,12 +1,11 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; -import { console } from "forge-std/console.sol"; -import { DSTestPlus } from "solmate/test/utils/DSTestPlus.sol"; +import "forge-std/Test.sol"; import { Cast } from "../Cast.sol"; import "../Buffer.sol"; -contract BufferTest is DSTestPlus { +contract BufferTest is Test { function testAllocateBuffer() public { // !gasreport allocate a buffer Buffer buf = Buffer_.allocate(32); diff --git a/packages/store/src/test/Bytes.t.sol b/packages/store/src/test/Bytes.t.sol index 6bf641666d..25fb95d272 100644 --- a/packages/store/src/test/Bytes.t.sol +++ b/packages/store/src/test/Bytes.t.sol @@ -1,11 +1,10 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; -import { console } from "forge-std/console.sol"; -import { DSTestPlus } from "solmate/test/utils/DSTestPlus.sol"; +import "forge-std/Test.sol"; import { Bytes } from "../Bytes.sol"; -contract BytesTest is DSTestPlus { +contract BytesTest is Test { function testFromBytesArray() public { bytes[] memory input = new bytes[](2); input[0] = new bytes(32); diff --git a/packages/store/src/test/Gas.t.sol b/packages/store/src/test/Gas.t.sol index cf6455bee1..50a481994f 100644 --- a/packages/store/src/test/Gas.t.sol +++ b/packages/store/src/test/Gas.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; -import { console } from "forge-std/console.sol"; -import { DSTestPlus } from "solmate/test/utils/DSTestPlus.sol"; +import "forge-std/Test.sol"; import { Bytes } from "../Bytes.sol"; import { Buffer_ } from "../Buffer.sol"; @@ -17,7 +16,7 @@ contract SomeContract { function doSomethingWithBytes(bytes memory data) public {} } -contract GasTest is DSTestPlus { +contract GasTest is Test { SomeContract someContract = new SomeContract(); function testCompareAbiEncodeVsCustom() public { diff --git a/packages/store/src/test/MixedTable.t.sol b/packages/store/src/test/MixedTable.t.sol index 159d35292e..0c7597d44c 100644 --- a/packages/store/src/test/MixedTable.t.sol +++ b/packages/store/src/test/MixedTable.t.sol @@ -1,15 +1,14 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; -import { console } from "forge-std/console.sol"; -import { DSTestPlus } from "solmate/test/utils/DSTestPlus.sol"; +import "forge-std/Test.sol"; import { MixedTable, tableId as MixedTableId, Mixed } from "../tables/MixedTable.sol"; import { StoreCore } from "../StoreCore.sol"; import { SchemaType } from "../Types.sol"; import { StoreView } from "../StoreView.sol"; import { Schema } from "../Schema.sol"; -contract MixedTableTest is DSTestPlus, StoreView { +contract MixedTableTest is Test, StoreView { Mixed private testMixed; function testRegisterAndGetSchema() public { diff --git a/packages/store/src/test/PackedCounter.t.sol b/packages/store/src/test/PackedCounter.t.sol index 5bd4acd580..ab11e5cdcb 100644 --- a/packages/store/src/test/PackedCounter.t.sol +++ b/packages/store/src/test/PackedCounter.t.sol @@ -1,11 +1,10 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; -import { console } from "forge-std/console.sol"; -import { DSTestPlus } from "solmate/test/utils/DSTestPlus.sol"; +import "forge-std/Test.sol"; import { PackedCounter, PackedCounter_ } from "../PackedCounter.sol"; -contract PackedCounterTest is DSTestPlus { +contract PackedCounterTest is Test { function testTotal() public { uint16[] memory counters = new uint16[](4); counters[0] = 1; diff --git a/packages/store/src/test/RouteTable.t.sol b/packages/store/src/test/RouteTable.t.sol index e2674f0a3b..a701fe51ed 100644 --- a/packages/store/src/test/RouteTable.t.sol +++ b/packages/store/src/test/RouteTable.t.sol @@ -1,15 +1,14 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; -import { console } from "forge-std/console.sol"; -import { DSTestPlus } from "solmate/test/utils/DSTestPlus.sol"; +import "forge-std/Test.sol"; import { RouteTable, tableId as RouteTableId, Route } from "../tables/RouteTable.sol"; import { StoreCore } from "../StoreCore.sol"; import { SchemaType } from "../Types.sol"; import { StoreView } from "../StoreView.sol"; import { Schema } from "../Schema.sol"; -contract RouteTableTest is DSTestPlus, StoreView { +contract RouteTableTest is Test, StoreView { function testRegisterAndGetSchema() public { // !gasreport register RouteTable schema RouteTable.registerSchema(); diff --git a/packages/store/src/test/Schema.t.sol b/packages/store/src/test/Schema.t.sol index 2e4625a011..a18b0b6056 100644 --- a/packages/store/src/test/Schema.t.sol +++ b/packages/store/src/test/Schema.t.sol @@ -1,12 +1,11 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; -import { console } from "forge-std/console.sol"; -import { DSTestPlus } from "solmate/test/utils/DSTestPlus.sol"; +import "forge-std/Test.sol"; import { Schema, Schema_ } from "../Schema.sol"; import { SchemaType } from "../Types.sol"; -contract SchemaTest is DSTestPlus { +contract SchemaTest is Test { function testEncodeDecodeSchema() public { uint256 gas = gasleft(); Schema schema = Schema_.encode( diff --git a/packages/store/src/test/Storage.t.sol b/packages/store/src/test/Storage.t.sol index 2e3443ba89..5ce0df69db 100644 --- a/packages/store/src/test/Storage.t.sol +++ b/packages/store/src/test/Storage.t.sol @@ -1,14 +1,13 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; -import { console } from "forge-std/console.sol"; -import { DSTestPlus } from "solmate/test/utils/DSTestPlus.sol"; +import "forge-std/Test.sol"; import { Cast } from "../Cast.sol"; import { Storage } from "../Storage.sol"; import { Utils } from "../Utils.sol"; import { Bytes } from "../Bytes.sol"; -contract StorageTest is DSTestPlus { +contract StorageTest is Test { function testWriteRead() public { bytes memory data1 = bytes.concat( bytes1(0x01), diff --git a/packages/store/src/test/StoreCore.t.sol b/packages/store/src/test/StoreCore.t.sol index 7b61ea45a1..63b8f8c007 100644 --- a/packages/store/src/test/StoreCore.t.sol +++ b/packages/store/src/test/StoreCore.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; -import { console } from "forge-std/console.sol"; -import { DSTestPlus } from "solmate/test/utils/DSTestPlus.sol"; +import "forge-std/Test.sol"; import { StoreCore } from "../StoreCore.sol"; import { Utils } from "../Utils.sol"; import { Bytes } from "../Bytes.sol"; @@ -23,7 +22,7 @@ struct TestStruct { uint32[] thirdData; } -contract StoreCoreTest is DSTestPlus, StoreView { +contract StoreCoreTest is Test, StoreView { TestStruct private testStruct; mapping(uint256 => bytes) private testMapping; diff --git a/packages/store/src/test/StoreSwitch.t.sol b/packages/store/src/test/StoreSwitch.t.sol index 81e99643cf..f7de358a91 100644 --- a/packages/store/src/test/StoreSwitch.t.sol +++ b/packages/store/src/test/StoreSwitch.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; -import { console } from "forge-std/console.sol"; -import { DSTestPlus } from "solmate/test/utils/DSTestPlus.sol"; +import "forge-std/Test.sol"; import { SchemaType } from "../Types.sol"; import { StoreCore } from "../StoreCore.sol"; import { StoreView } from "../StoreView.sol"; @@ -33,7 +32,7 @@ contract MockSystem { } } -contract StoreSwitchTest is DSTestPlus { +contract StoreSwitchTest is Test { Store store; function setUp() public { diff --git a/packages/store/src/test/System.t.sol b/packages/store/src/test/System.t.sol index 0d22571426..fffad45ef6 100644 --- a/packages/store/src/test/System.t.sol +++ b/packages/store/src/test/System.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; -import { console } from "forge-std/console.sol"; -import { DSTestPlus } from "solmate/test/utils/DSTestPlus.sol"; +import "forge-std/Test.sol"; import { World } from "../World.sol"; import { System } from "../System.sol"; @@ -14,7 +13,7 @@ contract TestSystem is System { } } -contract SystemTest is DSTestPlus { +contract SystemTest is Test { function testMsgSender() public { TestSystem system = new TestSystem(); address sender = address(0x123); diff --git a/packages/store/src/test/Vector2Table.t.sol b/packages/store/src/test/Vector2Table.t.sol index 24860b646a..3e6cc98b99 100644 --- a/packages/store/src/test/Vector2Table.t.sol +++ b/packages/store/src/test/Vector2Table.t.sol @@ -1,15 +1,14 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; -import { console } from "forge-std/console.sol"; -import { DSTestPlus } from "solmate/test/utils/DSTestPlus.sol"; +import "forge-std/Test.sol"; import { Vector2Table, tableId as Vector2Id, Vector2 } from "../tables/Vector2Table.sol"; import { StoreCore } from "../StoreCore.sol"; import { SchemaType } from "../Types.sol"; import { StoreView } from "../StoreView.sol"; import { Schema } from "../Schema.sol"; -contract Vector2TableTest is DSTestPlus, StoreView { +contract Vector2TableTest is Test, StoreView { function testRegisterAndGetSchema() public { // !gasreport register Vector2Table schema Vector2Table.registerSchema(); diff --git a/packages/store/src/test/World.t.sol b/packages/store/src/test/World.t.sol index 4a54e487d8..fd456ca026 100644 --- a/packages/store/src/test/World.t.sol +++ b/packages/store/src/test/World.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; -import { console } from "forge-std/console.sol"; -import { DSTestPlus } from "solmate/test/utils/DSTestPlus.sol"; +import "forge-std/Test.sol"; import { World } from "../World.sol"; import { System } from "../System.sol"; import { ExecutionMode } from "../Types.sol"; @@ -35,7 +34,7 @@ interface WorldWithWorldTestSystem { ) external; } -contract WorldTest is DSTestPlus { +contract WorldTest is Test { World internal world; WorldTestSystem internal system; diff --git a/packages/store/src/test/schemas/AddressArray.t.sol b/packages/store/src/test/schemas/AddressArray.t.sol index e93201c2b3..0fc5cf59e3 100644 --- a/packages/store/src/test/schemas/AddressArray.t.sol +++ b/packages/store/src/test/schemas/AddressArray.t.sol @@ -1,14 +1,13 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; -import { console } from "forge-std/console.sol"; -import { DSTestPlus } from "solmate/test/utils/DSTestPlus.sol"; +import "forge-std/Test.sol"; import { StoreView } from "../../StoreView.sol"; import { AddressArray, AddressArray_ } from "../../schemas/AddressArray.sol"; bytes32 constant tableId = keccak256("mud.store.table.addressArray"); -contract AddressArrayTest is DSTestPlus, StoreView { +contract AddressArrayTest is Test, StoreView { function testSetAndGet() public { AddressArray_.registerSchema(tableId); bytes32 key = keccak256("somekey"); diff --git a/packages/store/src/test/schemas/CallbackArray.t.sol b/packages/store/src/test/schemas/CallbackArray.t.sol index 7e739eaa79..90b5d5ecdc 100644 --- a/packages/store/src/test/schemas/CallbackArray.t.sol +++ b/packages/store/src/test/schemas/CallbackArray.t.sol @@ -1,14 +1,13 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; -import { console } from "forge-std/console.sol"; -import { DSTestPlus } from "solmate/test/utils/DSTestPlus.sol"; +import "forge-std/Test.sol"; import { StoreView } from "../../StoreView.sol"; import { CallbackArray, CallbackArray_ } from "../../schemas/CallbackArray.sol"; bytes32 constant tableId = keccak256("mud.store.table.callbackArray"); -contract CallbackArrayTest is DSTestPlus, StoreView { +contract CallbackArrayTest is Test, StoreView { function testSetAndGet() public { CallbackArray_.registerSchema(tableId); bytes32 key = keccak256("somekey"); From 625bc4ddf05d2278ea41e8695eaa5619d4aa113e Mon Sep 17 00:00:00 2001 From: alvrs Date: Wed, 1 Feb 2023 17:45:30 +0100 Subject: [PATCH 42/82] build(store): bring back ds-test bc forge-std actually needs it --- packages/store/package.json | 2 ++ packages/store/remappings.txt | 1 + 2 files changed, 3 insertions(+) diff --git a/packages/store/package.json b/packages/store/package.json index 59dd94fd05..3c673c5b50 100644 --- a/packages/store/package.json +++ b/packages/store/package.json @@ -27,6 +27,7 @@ "devDependencies": { "@typechain/ethers-v5": "^9.0.0", "@types/mocha": "^9.1.1", + "ds-test": "https://github.com/dapphub/ds-test.git#c9ce3f25bde29fc5eb9901842bf02850dfd2d084", "forge-std": "https://github.com/foundry-rs/forge-std.git#f36dab24d63d1c1945a05ed375ce341d3c1a49ed", "hardhat": "^2.10.1", "prettier": "^2.6.2", @@ -40,6 +41,7 @@ "@latticexyz/cli": "^1.34.0" }, "peerDependencies": { + "ds-test": "https://github.com/dapphub/ds-test.git#c9ce3f25bde29fc5eb9901842bf02850dfd2d084", "forge-std": "https://github.com/foundry-rs/forge-std.git#f36dab24d63d1c1945a05ed375ce341d3c1a49ed" }, "gitHead": "218f56893d268b0c5157a3e4c603b859e287a343" diff --git a/packages/store/remappings.txt b/packages/store/remappings.txt index f4a3a9687a..aeeced7eb6 100644 --- a/packages/store/remappings.txt +++ b/packages/store/remappings.txt @@ -1 +1,2 @@ +ds-test/=../../node_modules/ds-test/src/ forge-std/=../../node_modules/forge-std/src/ From 608d00a589b103870d8ad2597bb98ba8347bdde8 Mon Sep 17 00:00:00 2001 From: alvrs Date: Wed, 1 Feb 2023 17:59:02 +0100 Subject: [PATCH 43/82] build: use local mud cli in gasreport action --- .github/workflows/gasreport.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/gasreport.yml b/.github/workflows/gasreport.yml index 12eb13ae8c..eb6bbf952d 100644 --- a/.github/workflows/gasreport.yml +++ b/.github/workflows/gasreport.yml @@ -18,6 +18,9 @@ jobs: - name: git-checkout uses: actions/checkout@v2 + - name: Install local CLI + run: cd packages/cli && yarn build && npm link && cd ../.. + - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 with: From 4d74ddea223303a3f01624c1ac1b93ea79b30298 Mon Sep 17 00:00:00 2001 From: alvrs Date: Wed, 1 Feb 2023 18:04:18 +0100 Subject: [PATCH 44/82] build: gas report action - install cli dependencies before building --- .github/workflows/gasreport.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gasreport.yml b/.github/workflows/gasreport.yml index eb6bbf952d..36a0c6585f 100644 --- a/.github/workflows/gasreport.yml +++ b/.github/workflows/gasreport.yml @@ -19,7 +19,7 @@ jobs: uses: actions/checkout@v2 - name: Install local CLI - run: cd packages/cli && yarn build && npm link && cd ../.. + run: cd packages/cli && yarn && yarn build && npm link && cd ../.. - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 From 0146455cca1dd2bf195aa8cb32dde4c15eff059c Mon Sep 17 00:00:00 2001 From: alvrs Date: Wed, 1 Feb 2023 18:08:23 +0100 Subject: [PATCH 45/82] build: gas report action - move cli linking action after general building --- .github/workflows/gasreport.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/gasreport.yml b/.github/workflows/gasreport.yml index 36a0c6585f..aac9d2cea1 100644 --- a/.github/workflows/gasreport.yml +++ b/.github/workflows/gasreport.yml @@ -18,9 +18,6 @@ jobs: - name: git-checkout uses: actions/checkout@v2 - - name: Install local CLI - run: cd packages/cli && yarn && yarn build && npm link && cd ../.. - - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 with: @@ -29,6 +26,9 @@ jobs: - name: Install dependencies run: yarn install --network-concurrency 1 + - name: Install local CLI + run: cd packages/cli && yarn build && npm link && cd ../.. + - name: Run gas report run: yarn workspace @latticexyz/store run gasreport From 40c512dbfed75a1ac79b2936aeddf2064020feca Mon Sep 17 00:00:00 2001 From: alvrs Date: Wed, 1 Feb 2023 18:24:25 +0100 Subject: [PATCH 46/82] build: gas report action - force gh to use local cli --- packages/store/package.json | 2 +- .../store/src/test/BytesMudGasReport.t.sol | 462 ++++++++++++++++++ 2 files changed, 463 insertions(+), 1 deletion(-) create mode 100644 packages/store/src/test/BytesMudGasReport.t.sol diff --git a/packages/store/package.json b/packages/store/package.json index 3c673c5b50..3808c29008 100644 --- a/packages/store/package.json +++ b/packages/store/package.json @@ -22,7 +22,7 @@ "link": "yarn link", "docs": "rimraf API && hardhat docgen && echo 'label: API\norder: 50' > API/index.yml", "release": "npm publish || echo 'version already published'", - "gasreport": "yarn @latticexyz/cli gas-report --path src/test/** --save gas-report.txt" + "gasreport": " ../cli/dist/index.js gas-report --path src/test/** --save gas-report.txt" }, "devDependencies": { "@typechain/ethers-v5": "^9.0.0", diff --git a/packages/store/src/test/BytesMudGasReport.t.sol b/packages/store/src/test/BytesMudGasReport.t.sol new file mode 100644 index 0000000000..5a6d9705ae --- /dev/null +++ b/packages/store/src/test/BytesMudGasReport.t.sol @@ -0,0 +1,462 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import "forge-std/Test.sol"; +import { Bytes } from "../Bytes.sol"; + +contract BytesTest is Test { + function testFromBytesArray() public { + uint256 _gasreport; + bytes[] memory input = new bytes[](2); + input[0] = new bytes(32); + input[0][0] = 0x01; + input[0][31] = 0x02; + input[1] = new bytes(32); + input[1][0] = 0x03; + input[1][31] = 0x04; + + _gasreport = gasleft(); + bytes memory output = Bytes.from(input); + _gasreport = _gasreport - gasleft(); + console.log("GAS REPORT: create bytes from bytes array [bytes memory output = Bytes.from(input);]:", _gasreport); + + assertEq(output.length, 64); + assertEq(uint256(Bytes.toBytes32(output, 0)), 0x0100000000000000000000000000000000000000000000000000000000000002); + assertEq(uint256(Bytes.toBytes32(output, 32)), 0x0300000000000000000000000000000000000000000000000000000000000004); + } + + function testFromUint8Array() public { + uint256 _gasreport; + uint8[] memory input = new uint8[](2); + input[0] = 0x01; + input[1] = 0x02; + + _gasreport = gasleft(); + bytes memory output = Bytes.from(input); + _gasreport = _gasreport - gasleft(); + console.log("GAS REPORT: create bytes from uint8 array [bytes memory output = Bytes.from(input);]:", _gasreport); + + assertEq(output.length, 2); + assertEq(uint256(uint8(output[0])), 0x01); + assertEq(uint256(uint8(output[1])), 0x02); + } + + function testFromUint16Array() public { + uint256 _gasreport; + uint16[] memory input = new uint16[](3); + input[0] = 0x0102; + input[1] = 0x0304; + input[2] = 0x0506; + + _gasreport = gasleft(); + bytes memory output = Bytes.from(input); + _gasreport = _gasreport - gasleft(); + console.log("GAS REPORT: create bytes from uint16 array [bytes memory output = Bytes.from(input);]:", _gasreport); + + assertEq(output.length, 6); + assertEq(uint256(uint8(output[0])), 0x01); + assertEq(uint256(uint8(output[1])), 0x02); + assertEq(uint256(uint8(output[2])), 0x03); + assertEq(uint256(uint8(output[3])), 0x04); + assertEq(uint256(uint8(output[4])), 0x05); + assertEq(uint256(uint8(output[5])), 0x06); + } + + function testFromUint32Array() public { + uint256 _gasreport; + uint32[] memory input = new uint32[](2); + input[0] = 0x01020304; + input[1] = 0x05060708; + + _gasreport = gasleft(); + bytes memory output = Bytes.from(input); + _gasreport = _gasreport - gasleft(); + console.log("GAS REPORT: create bytes from uint32 array [bytes memory output = Bytes.from(input);]:", _gasreport); + + assertEq(output.length, 8); + assertEq(uint256(uint8(output[0])), 0x01); + assertEq(uint256(uint8(output[1])), 0x02); + assertEq(uint256(uint8(output[2])), 0x03); + assertEq(uint256(uint8(output[3])), 0x04); + assertEq(uint256(uint8(output[4])), 0x05); + assertEq(uint256(uint8(output[5])), 0x06); + assertEq(uint256(uint8(output[6])), 0x07); + assertEq(uint256(uint8(output[7])), 0x08); + } + + function testToBytes32() public { + uint256 _gasreport; + bytes memory input = new bytes(32); + input[0] = 0x01; + input[31] = 0x02; + + _gasreport = gasleft(); + bytes32 output = Bytes.toBytes32(input, 0); + _gasreport = _gasreport - gasleft(); + console.log( + "GAS REPORT: create bytes32 from bytes memory with offset 0 [bytes32 output = Bytes.toBytes32(input, 0);]:", + _gasreport + ); + + assertEq(uint256(output), 0x0100000000000000000000000000000000000000000000000000000000000002); + } + + function testToBytes32CrossWord() public { + uint256 _gasreport; + bytes memory input = new bytes(64); + input[0 + 16] = 0x01; + input[31 + 16] = 0x02; + + _gasreport = gasleft(); + bytes32 output = Bytes.toBytes32(input, 16); + _gasreport = _gasreport - gasleft(); + console.log( + "GAS REPORT: create bytes32 from bytes memory with offset 16 [bytes32 output = Bytes.toBytes32(input, 16);]:", + _gasreport + ); + + assertEq(uint256(output), 0x0100000000000000000000000000000000000000000000000000000000000002); + } + + function testToBytes32Array() public { + uint256 _gasreport; + bytes memory input = new bytes(64); + input[0] = 0x01; + input[31] = 0x02; + input[32] = 0x03; + input[63] = 0x04; + + _gasreport = gasleft(); + bytes32[] memory output = Bytes.toBytes32Array(input); + _gasreport = _gasreport - gasleft(); + console.log( + "GAS REPORT: create bytes32 array from bytes memory [bytes32[] memory output = Bytes.toBytes32Array(input);]:", + _gasreport + ); + + assertEq(output.length, 2); + assertEq(uint256(output[0]), 0x0100000000000000000000000000000000000000000000000000000000000002); + assertEq(uint256(output[1]), 0x0300000000000000000000000000000000000000000000000000000000000004); + } + + function testFromAndToUint32Array() public { + uint256 _gasreport; + uint32[] memory input = new uint32[](2); + input[0] = 0x01020304; + input[1] = 0x05060708; + + bytes memory tight = Bytes.from(input); + assertEq(tight.length, 8); + + _gasreport = gasleft(); + uint32[] memory output = Bytes.toUint32Array(tight); + _gasreport = _gasreport - gasleft(); + console.log( + "GAS REPORT: create uint32 array from bytes memory [uint32[] memory output = Bytes.toUint32Array(tight);]:", + _gasreport + ); + + assertEq(output.length, 2); + assertEq(output[0], 0x01020304); + assertEq(output[1], 0x05060708); + } + + function testToAndFromBytes24Array() public { + uint256 _gasreport; + bytes24[] memory input = new bytes24[](2); + input[0] = bytes24(0x0102030405060708090a0b0c0d0e0f101112131415161718); + input[1] = bytes24(0x19202122232425262728292a2b2c2d2e2f30313233343536); + + _gasreport = gasleft(); + bytes memory tight = Bytes.from(input); + _gasreport = _gasreport - gasleft(); + console.log( + "GAS REPORT: tightly pack bytes24 array into bytes array [bytes memory tight = Bytes.from(input);]:", + _gasreport + ); + + assertEq(tight.length, 48); + + _gasreport = gasleft(); + bytes24[] memory output = Bytes.toBytes24Array(tight); + _gasreport = _gasreport - gasleft(); + console.log( + "GAS REPORT: create uint32 array from bytes memory [bytes24[] memory output = Bytes.toBytes24Array(tight);]:", + _gasreport + ); + + assertEq(output.length, 2); + assertEq(output[0], input[0]); + assertEq(output[1], input[1]); + } + + function testToBytes32ArrayUneven() public { + uint256 _gasreport; + bytes memory input = new bytes(65); + input[0] = 0x01; + input[31] = 0x02; + input[32] = 0x03; + input[63] = 0x04; + input[64] = 0x05; + + _gasreport = gasleft(); + bytes32[] memory output = Bytes.toBytes32Array(input); + _gasreport = _gasreport - gasleft(); + console.log( + "GAS REPORT: create bytes32 array from bytes memory with uneven length [bytes32[] memory output = Bytes.toBytes32Array(input);]:", + _gasreport + ); + + assertEq(output.length, 3); + assertEq(uint256(output[0]), 0x0100000000000000000000000000000000000000000000000000000000000002); + assertEq(uint256(output[1]), 0x0300000000000000000000000000000000000000000000000000000000000004); + assertEq(uint256(output[2]), 0x0500000000000000000000000000000000000000000000000000000000000000); + } + + function testFromAndToUint32() public { + uint256 _gasreport; + uint32 input = 0x01000002; + + _gasreport = gasleft(); + bytes memory output = Bytes.from(input); + _gasreport = _gasreport - gasleft(); + console.log("GAS REPORT: create bytes from uint32 [bytes memory output = Bytes.from(input);]:", _gasreport); + + assertEq(output.length, 4); + + _gasreport = gasleft(); + uint32 output2 = Bytes.toUint32(output); + _gasreport = _gasreport - gasleft(); + console.log("GAS REPORT: create uint32 from bytes [uint32 output2 = Bytes.toUint32(output);]:", _gasreport); + + assertEq(output2, input); + } + + function testFromAndToUint32Fuzzy(uint32 input) public { + uint256 _gasreport; + assertEq(Bytes.toUint32(Bytes.from(input)), input); + } + + function testFromAndToAddress() public { + uint256 _gasreport; + address input = address(0x0100000000000000000000000000000000000002); + + _gasreport = gasleft(); + bytes memory output = Bytes.from(input); + _gasreport = _gasreport - gasleft(); + console.log("GAS REPORT: create bytes from address [bytes memory output = Bytes.from(input);]:", _gasreport); + + assertEq(output.length, 20); + + _gasreport = gasleft(); + address output2 = Bytes.toAddress(output); + _gasreport = _gasreport - gasleft(); + console.log("GAS REPORT: create address from bytes [address output2 = Bytes.toAddress(output);]:", _gasreport); + + assertEq(output2, input); + } + + function testFromAndToAddressFuzzy(address input) public { + uint256 _gasreport; + assertEq(Bytes.toAddress(Bytes.from(input)), input); + } + + function testFromAndToUint8() public { + uint256 _gasreport; + uint8 input = 0x02; + + _gasreport = gasleft(); + bytes memory output = Bytes.fromUint8(input); + _gasreport = _gasreport - gasleft(); + console.log("GAS REPORT: create bytes from uint8 [bytes memory output = Bytes.fromUint8(input);]:", _gasreport); + + assertEq(output.length, 1); + + _gasreport = gasleft(); + uint8 output2 = Bytes.toUint8(output); + _gasreport = _gasreport - gasleft(); + console.log("GAS REPORT: create uint8 from bytes [uint8 output2 = Bytes.toUint8(output);]:", _gasreport); + + assertEq(output2, input); + } + + function testFromAndToUint8Fuzzy(uint8 input) public { + uint256 _gasreport; + assertEq(Bytes.toUint8(Bytes.fromUint8(input)), input); + } + + function testFromAndToBytes4() public { + uint256 _gasreport; + bytes4 input = bytes4(0x01000002); + + _gasreport = gasleft(); + bytes memory output = Bytes.from(input); + _gasreport = _gasreport - gasleft(); + console.log("GAS REPORT: create bytes from bytes4 [bytes memory output = Bytes.from(input);]:", _gasreport); + + assertEq(output.length, 4); + + _gasreport = gasleft(); + bytes4 output2 = Bytes.toBytes4(output); + _gasreport = _gasreport - gasleft(); + console.log("GAS REPORT: create bytes4 from bytes [bytes4 output2 = Bytes.toBytes4(output);]:", _gasreport); + + assertEq(output2, input); + } + + function testFromAndToBytes4Fuzzy(bytes4 input) public { + uint256 _gasreport; + assertEq(Bytes.toBytes4(Bytes.from(input)), input); + } + + function testEquals() public { + uint256 _gasreport; + bytes memory a = bytes("a"); + bytes memory b = bytes("a"); + + _gasreport = gasleft(); + bool equals = Bytes.equals(a, b); + _gasreport = _gasreport - gasleft(); + console.log("GAS REPORT: compare equal bytes [bool equals = Bytes.equals(a, b);]:", _gasreport); + + assertTrue(equals); + } + + function testEqualsFalse() public { + uint256 _gasreport; + bytes memory a = bytes("a"); + bytes memory b = bytes("b"); + + _gasreport = gasleft(); + bool equals = Bytes.equals(a, b); + _gasreport = _gasreport - gasleft(); + console.log("GAS REPORT: compare unequal bytes [bool equals = Bytes.equals(a, b);]:", _gasreport); + + assertFalse(equals); + } + + function testEqualsFalseDiffLength() public { + uint256 _gasreport; + bytes memory a = bytes("a"); + bytes memory b = bytes("aa"); + assertFalse(Bytes.equals(a, b)); + } + + function testSetLengthInPlace() public { + uint256 _gasreport; + bytes memory a = new bytes(5); + assertEq(a.length, 5); + + _gasreport = gasleft(); + Bytes.setLengthInPlace(a, 2); + _gasreport = _gasreport - gasleft(); + console.log("GAS REPORT: set length of bytes in place [Bytes.setLengthInPlace(a, 2);]:", _gasreport); + + assertEq(a.length, 2); + } + + function testSlice() public { + uint256 _gasreport; + bytes memory a = new bytes(5); + a[0] = 0x01; + a[1] = 0x02; + a[2] = 0x03; + a[3] = 0x04; + a[4] = 0x05; + + _gasreport = gasleft(); + bytes memory b = Bytes.slice(a, 1, 3); + _gasreport = _gasreport - gasleft(); + console.log( + "GAS REPORT: slice bytes (with copying) with offset 1 and length 3 [bytes memory b = Bytes.slice(a, 1, 3);]:", + _gasreport + ); + + assertEq(b.length, 3); + assertEq(uint256(uint8(b[0])), 0x02); + assertEq(uint256(uint8(b[1])), 0x03); + assertEq(uint256(uint8(b[2])), 0x04); + } + + // TODO: add tests for other sliceX functions + function testSlice3() public { + uint256 _gasreport; + bytes memory a = new bytes(5); + a[0] = 0x01; + a[1] = 0x02; + a[2] = 0x03; + a[3] = 0x04; + a[4] = 0x05; + + _gasreport = gasleft(); + bytes3 b = Bytes.slice3(a, 1); + _gasreport = _gasreport - gasleft(); + console.log("GAS REPORT: slice bytes3 with offset 1 [bytes3 b = Bytes.slice3(a, 1);]:", _gasreport); + + assertEq(b.length, 3); + assertEq(uint256(uint8(b[0])), 0x02); + assertEq(uint256(uint8(b[1])), 0x03); + assertEq(uint256(uint8(b[2])), 0x04); + } + + function testSlice32() public { + uint256 _gasreport; + bytes32 original = keccak256("some data"); + bytes memory input = bytes.concat(bytes10(keccak256("irrelevant data")), original); + + _gasreport = gasleft(); + bytes32 output = Bytes.slice32(input, 10); + _gasreport = _gasreport - gasleft(); + console.log("GAS REPORT: slice bytes32 with offset 10 [bytes32 output = Bytes.slice32(input, 10);]:", _gasreport); + + assertEq(output, original); + } + + function testSetBytes1() public { + uint256 _gasreport; + bytes32 input = bytes32(0); + + _gasreport = gasleft(); + Bytes.setBytes1(input, 8, 0xff); + _gasreport = _gasreport - gasleft(); + console.log("GAS REPORT: set bytes1 in bytes32 [Bytes.setBytes1(input, 8, 0xff);]:", _gasreport); + + assertEq(Bytes.setBytes1(input, 0, 0x01), bytes32(bytes1(0x01))); + assertEq(Bytes.setBytes1(input, 31, 0x01), bytes32(uint256(0x01))); + } + + function testSetBytes2() public { + uint256 _gasreport; + bytes32 input = bytes32(0); + + _gasreport = gasleft(); + Bytes.setBytes2(input, 8, 0xffff); + _gasreport = _gasreport - gasleft(); + console.log("GAS REPORT: set bytes2 in bytes32 [Bytes.setBytes2(input, 8, 0xffff);]:", _gasreport); + + assertEq(Bytes.setBytes2(input, 0, 0xffff), bytes32(bytes2(0xffff))); + assertEq(Bytes.setBytes2(input, 30, 0xffff), bytes32(uint256(0xffff))); + } + + function testSetBytes4() public { + uint256 _gasreport; + bytes32 input = bytes32(0); + + _gasreport = gasleft(); + Bytes.setBytes4(input, 8, 0xffffffff); + _gasreport = _gasreport - gasleft(); + console.log("GAS REPORT: set bytes4 in bytes32 [Bytes.setBytes4(input, 8, 0xffffffff);]:", _gasreport); + + assertEq(Bytes.setBytes4(input, 0, 0xffffffff), bytes32(bytes4(0xffffffff))); + assertEq(Bytes.setBytes4(input, 30, 0xffffffff), bytes32(uint256(0xffff))); + assertEq(Bytes.setBytes4(input, 28, 0xffffffff), bytes32(uint256(0xffffffff))); + + bytes32 input2 = bytes32(0x0000000a000a0000000000000000000000000000000000000000000000000000); + bytes4 overwrite = bytes4(0x0000006d); + + assertEq( + Bytes.setBytes4(input2, 0, overwrite), + bytes32(0x0000006d000a0000000000000000000000000000000000000000000000000000) + ); + } +} From f75a52c8e008af84e4bdb046e20940018a7e1680 Mon Sep 17 00:00:00 2001 From: alvrs Date: Wed, 1 Feb 2023 18:50:21 +0100 Subject: [PATCH 47/82] fix(store): remove sneaked in temp test file --- .../store/src/test/BytesMudGasReport.t.sol | 462 ------------------ 1 file changed, 462 deletions(-) delete mode 100644 packages/store/src/test/BytesMudGasReport.t.sol diff --git a/packages/store/src/test/BytesMudGasReport.t.sol b/packages/store/src/test/BytesMudGasReport.t.sol deleted file mode 100644 index 5a6d9705ae..0000000000 --- a/packages/store/src/test/BytesMudGasReport.t.sol +++ /dev/null @@ -1,462 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.8.0; - -import "forge-std/Test.sol"; -import { Bytes } from "../Bytes.sol"; - -contract BytesTest is Test { - function testFromBytesArray() public { - uint256 _gasreport; - bytes[] memory input = new bytes[](2); - input[0] = new bytes(32); - input[0][0] = 0x01; - input[0][31] = 0x02; - input[1] = new bytes(32); - input[1][0] = 0x03; - input[1][31] = 0x04; - - _gasreport = gasleft(); - bytes memory output = Bytes.from(input); - _gasreport = _gasreport - gasleft(); - console.log("GAS REPORT: create bytes from bytes array [bytes memory output = Bytes.from(input);]:", _gasreport); - - assertEq(output.length, 64); - assertEq(uint256(Bytes.toBytes32(output, 0)), 0x0100000000000000000000000000000000000000000000000000000000000002); - assertEq(uint256(Bytes.toBytes32(output, 32)), 0x0300000000000000000000000000000000000000000000000000000000000004); - } - - function testFromUint8Array() public { - uint256 _gasreport; - uint8[] memory input = new uint8[](2); - input[0] = 0x01; - input[1] = 0x02; - - _gasreport = gasleft(); - bytes memory output = Bytes.from(input); - _gasreport = _gasreport - gasleft(); - console.log("GAS REPORT: create bytes from uint8 array [bytes memory output = Bytes.from(input);]:", _gasreport); - - assertEq(output.length, 2); - assertEq(uint256(uint8(output[0])), 0x01); - assertEq(uint256(uint8(output[1])), 0x02); - } - - function testFromUint16Array() public { - uint256 _gasreport; - uint16[] memory input = new uint16[](3); - input[0] = 0x0102; - input[1] = 0x0304; - input[2] = 0x0506; - - _gasreport = gasleft(); - bytes memory output = Bytes.from(input); - _gasreport = _gasreport - gasleft(); - console.log("GAS REPORT: create bytes from uint16 array [bytes memory output = Bytes.from(input);]:", _gasreport); - - assertEq(output.length, 6); - assertEq(uint256(uint8(output[0])), 0x01); - assertEq(uint256(uint8(output[1])), 0x02); - assertEq(uint256(uint8(output[2])), 0x03); - assertEq(uint256(uint8(output[3])), 0x04); - assertEq(uint256(uint8(output[4])), 0x05); - assertEq(uint256(uint8(output[5])), 0x06); - } - - function testFromUint32Array() public { - uint256 _gasreport; - uint32[] memory input = new uint32[](2); - input[0] = 0x01020304; - input[1] = 0x05060708; - - _gasreport = gasleft(); - bytes memory output = Bytes.from(input); - _gasreport = _gasreport - gasleft(); - console.log("GAS REPORT: create bytes from uint32 array [bytes memory output = Bytes.from(input);]:", _gasreport); - - assertEq(output.length, 8); - assertEq(uint256(uint8(output[0])), 0x01); - assertEq(uint256(uint8(output[1])), 0x02); - assertEq(uint256(uint8(output[2])), 0x03); - assertEq(uint256(uint8(output[3])), 0x04); - assertEq(uint256(uint8(output[4])), 0x05); - assertEq(uint256(uint8(output[5])), 0x06); - assertEq(uint256(uint8(output[6])), 0x07); - assertEq(uint256(uint8(output[7])), 0x08); - } - - function testToBytes32() public { - uint256 _gasreport; - bytes memory input = new bytes(32); - input[0] = 0x01; - input[31] = 0x02; - - _gasreport = gasleft(); - bytes32 output = Bytes.toBytes32(input, 0); - _gasreport = _gasreport - gasleft(); - console.log( - "GAS REPORT: create bytes32 from bytes memory with offset 0 [bytes32 output = Bytes.toBytes32(input, 0);]:", - _gasreport - ); - - assertEq(uint256(output), 0x0100000000000000000000000000000000000000000000000000000000000002); - } - - function testToBytes32CrossWord() public { - uint256 _gasreport; - bytes memory input = new bytes(64); - input[0 + 16] = 0x01; - input[31 + 16] = 0x02; - - _gasreport = gasleft(); - bytes32 output = Bytes.toBytes32(input, 16); - _gasreport = _gasreport - gasleft(); - console.log( - "GAS REPORT: create bytes32 from bytes memory with offset 16 [bytes32 output = Bytes.toBytes32(input, 16);]:", - _gasreport - ); - - assertEq(uint256(output), 0x0100000000000000000000000000000000000000000000000000000000000002); - } - - function testToBytes32Array() public { - uint256 _gasreport; - bytes memory input = new bytes(64); - input[0] = 0x01; - input[31] = 0x02; - input[32] = 0x03; - input[63] = 0x04; - - _gasreport = gasleft(); - bytes32[] memory output = Bytes.toBytes32Array(input); - _gasreport = _gasreport - gasleft(); - console.log( - "GAS REPORT: create bytes32 array from bytes memory [bytes32[] memory output = Bytes.toBytes32Array(input);]:", - _gasreport - ); - - assertEq(output.length, 2); - assertEq(uint256(output[0]), 0x0100000000000000000000000000000000000000000000000000000000000002); - assertEq(uint256(output[1]), 0x0300000000000000000000000000000000000000000000000000000000000004); - } - - function testFromAndToUint32Array() public { - uint256 _gasreport; - uint32[] memory input = new uint32[](2); - input[0] = 0x01020304; - input[1] = 0x05060708; - - bytes memory tight = Bytes.from(input); - assertEq(tight.length, 8); - - _gasreport = gasleft(); - uint32[] memory output = Bytes.toUint32Array(tight); - _gasreport = _gasreport - gasleft(); - console.log( - "GAS REPORT: create uint32 array from bytes memory [uint32[] memory output = Bytes.toUint32Array(tight);]:", - _gasreport - ); - - assertEq(output.length, 2); - assertEq(output[0], 0x01020304); - assertEq(output[1], 0x05060708); - } - - function testToAndFromBytes24Array() public { - uint256 _gasreport; - bytes24[] memory input = new bytes24[](2); - input[0] = bytes24(0x0102030405060708090a0b0c0d0e0f101112131415161718); - input[1] = bytes24(0x19202122232425262728292a2b2c2d2e2f30313233343536); - - _gasreport = gasleft(); - bytes memory tight = Bytes.from(input); - _gasreport = _gasreport - gasleft(); - console.log( - "GAS REPORT: tightly pack bytes24 array into bytes array [bytes memory tight = Bytes.from(input);]:", - _gasreport - ); - - assertEq(tight.length, 48); - - _gasreport = gasleft(); - bytes24[] memory output = Bytes.toBytes24Array(tight); - _gasreport = _gasreport - gasleft(); - console.log( - "GAS REPORT: create uint32 array from bytes memory [bytes24[] memory output = Bytes.toBytes24Array(tight);]:", - _gasreport - ); - - assertEq(output.length, 2); - assertEq(output[0], input[0]); - assertEq(output[1], input[1]); - } - - function testToBytes32ArrayUneven() public { - uint256 _gasreport; - bytes memory input = new bytes(65); - input[0] = 0x01; - input[31] = 0x02; - input[32] = 0x03; - input[63] = 0x04; - input[64] = 0x05; - - _gasreport = gasleft(); - bytes32[] memory output = Bytes.toBytes32Array(input); - _gasreport = _gasreport - gasleft(); - console.log( - "GAS REPORT: create bytes32 array from bytes memory with uneven length [bytes32[] memory output = Bytes.toBytes32Array(input);]:", - _gasreport - ); - - assertEq(output.length, 3); - assertEq(uint256(output[0]), 0x0100000000000000000000000000000000000000000000000000000000000002); - assertEq(uint256(output[1]), 0x0300000000000000000000000000000000000000000000000000000000000004); - assertEq(uint256(output[2]), 0x0500000000000000000000000000000000000000000000000000000000000000); - } - - function testFromAndToUint32() public { - uint256 _gasreport; - uint32 input = 0x01000002; - - _gasreport = gasleft(); - bytes memory output = Bytes.from(input); - _gasreport = _gasreport - gasleft(); - console.log("GAS REPORT: create bytes from uint32 [bytes memory output = Bytes.from(input);]:", _gasreport); - - assertEq(output.length, 4); - - _gasreport = gasleft(); - uint32 output2 = Bytes.toUint32(output); - _gasreport = _gasreport - gasleft(); - console.log("GAS REPORT: create uint32 from bytes [uint32 output2 = Bytes.toUint32(output);]:", _gasreport); - - assertEq(output2, input); - } - - function testFromAndToUint32Fuzzy(uint32 input) public { - uint256 _gasreport; - assertEq(Bytes.toUint32(Bytes.from(input)), input); - } - - function testFromAndToAddress() public { - uint256 _gasreport; - address input = address(0x0100000000000000000000000000000000000002); - - _gasreport = gasleft(); - bytes memory output = Bytes.from(input); - _gasreport = _gasreport - gasleft(); - console.log("GAS REPORT: create bytes from address [bytes memory output = Bytes.from(input);]:", _gasreport); - - assertEq(output.length, 20); - - _gasreport = gasleft(); - address output2 = Bytes.toAddress(output); - _gasreport = _gasreport - gasleft(); - console.log("GAS REPORT: create address from bytes [address output2 = Bytes.toAddress(output);]:", _gasreport); - - assertEq(output2, input); - } - - function testFromAndToAddressFuzzy(address input) public { - uint256 _gasreport; - assertEq(Bytes.toAddress(Bytes.from(input)), input); - } - - function testFromAndToUint8() public { - uint256 _gasreport; - uint8 input = 0x02; - - _gasreport = gasleft(); - bytes memory output = Bytes.fromUint8(input); - _gasreport = _gasreport - gasleft(); - console.log("GAS REPORT: create bytes from uint8 [bytes memory output = Bytes.fromUint8(input);]:", _gasreport); - - assertEq(output.length, 1); - - _gasreport = gasleft(); - uint8 output2 = Bytes.toUint8(output); - _gasreport = _gasreport - gasleft(); - console.log("GAS REPORT: create uint8 from bytes [uint8 output2 = Bytes.toUint8(output);]:", _gasreport); - - assertEq(output2, input); - } - - function testFromAndToUint8Fuzzy(uint8 input) public { - uint256 _gasreport; - assertEq(Bytes.toUint8(Bytes.fromUint8(input)), input); - } - - function testFromAndToBytes4() public { - uint256 _gasreport; - bytes4 input = bytes4(0x01000002); - - _gasreport = gasleft(); - bytes memory output = Bytes.from(input); - _gasreport = _gasreport - gasleft(); - console.log("GAS REPORT: create bytes from bytes4 [bytes memory output = Bytes.from(input);]:", _gasreport); - - assertEq(output.length, 4); - - _gasreport = gasleft(); - bytes4 output2 = Bytes.toBytes4(output); - _gasreport = _gasreport - gasleft(); - console.log("GAS REPORT: create bytes4 from bytes [bytes4 output2 = Bytes.toBytes4(output);]:", _gasreport); - - assertEq(output2, input); - } - - function testFromAndToBytes4Fuzzy(bytes4 input) public { - uint256 _gasreport; - assertEq(Bytes.toBytes4(Bytes.from(input)), input); - } - - function testEquals() public { - uint256 _gasreport; - bytes memory a = bytes("a"); - bytes memory b = bytes("a"); - - _gasreport = gasleft(); - bool equals = Bytes.equals(a, b); - _gasreport = _gasreport - gasleft(); - console.log("GAS REPORT: compare equal bytes [bool equals = Bytes.equals(a, b);]:", _gasreport); - - assertTrue(equals); - } - - function testEqualsFalse() public { - uint256 _gasreport; - bytes memory a = bytes("a"); - bytes memory b = bytes("b"); - - _gasreport = gasleft(); - bool equals = Bytes.equals(a, b); - _gasreport = _gasreport - gasleft(); - console.log("GAS REPORT: compare unequal bytes [bool equals = Bytes.equals(a, b);]:", _gasreport); - - assertFalse(equals); - } - - function testEqualsFalseDiffLength() public { - uint256 _gasreport; - bytes memory a = bytes("a"); - bytes memory b = bytes("aa"); - assertFalse(Bytes.equals(a, b)); - } - - function testSetLengthInPlace() public { - uint256 _gasreport; - bytes memory a = new bytes(5); - assertEq(a.length, 5); - - _gasreport = gasleft(); - Bytes.setLengthInPlace(a, 2); - _gasreport = _gasreport - gasleft(); - console.log("GAS REPORT: set length of bytes in place [Bytes.setLengthInPlace(a, 2);]:", _gasreport); - - assertEq(a.length, 2); - } - - function testSlice() public { - uint256 _gasreport; - bytes memory a = new bytes(5); - a[0] = 0x01; - a[1] = 0x02; - a[2] = 0x03; - a[3] = 0x04; - a[4] = 0x05; - - _gasreport = gasleft(); - bytes memory b = Bytes.slice(a, 1, 3); - _gasreport = _gasreport - gasleft(); - console.log( - "GAS REPORT: slice bytes (with copying) with offset 1 and length 3 [bytes memory b = Bytes.slice(a, 1, 3);]:", - _gasreport - ); - - assertEq(b.length, 3); - assertEq(uint256(uint8(b[0])), 0x02); - assertEq(uint256(uint8(b[1])), 0x03); - assertEq(uint256(uint8(b[2])), 0x04); - } - - // TODO: add tests for other sliceX functions - function testSlice3() public { - uint256 _gasreport; - bytes memory a = new bytes(5); - a[0] = 0x01; - a[1] = 0x02; - a[2] = 0x03; - a[3] = 0x04; - a[4] = 0x05; - - _gasreport = gasleft(); - bytes3 b = Bytes.slice3(a, 1); - _gasreport = _gasreport - gasleft(); - console.log("GAS REPORT: slice bytes3 with offset 1 [bytes3 b = Bytes.slice3(a, 1);]:", _gasreport); - - assertEq(b.length, 3); - assertEq(uint256(uint8(b[0])), 0x02); - assertEq(uint256(uint8(b[1])), 0x03); - assertEq(uint256(uint8(b[2])), 0x04); - } - - function testSlice32() public { - uint256 _gasreport; - bytes32 original = keccak256("some data"); - bytes memory input = bytes.concat(bytes10(keccak256("irrelevant data")), original); - - _gasreport = gasleft(); - bytes32 output = Bytes.slice32(input, 10); - _gasreport = _gasreport - gasleft(); - console.log("GAS REPORT: slice bytes32 with offset 10 [bytes32 output = Bytes.slice32(input, 10);]:", _gasreport); - - assertEq(output, original); - } - - function testSetBytes1() public { - uint256 _gasreport; - bytes32 input = bytes32(0); - - _gasreport = gasleft(); - Bytes.setBytes1(input, 8, 0xff); - _gasreport = _gasreport - gasleft(); - console.log("GAS REPORT: set bytes1 in bytes32 [Bytes.setBytes1(input, 8, 0xff);]:", _gasreport); - - assertEq(Bytes.setBytes1(input, 0, 0x01), bytes32(bytes1(0x01))); - assertEq(Bytes.setBytes1(input, 31, 0x01), bytes32(uint256(0x01))); - } - - function testSetBytes2() public { - uint256 _gasreport; - bytes32 input = bytes32(0); - - _gasreport = gasleft(); - Bytes.setBytes2(input, 8, 0xffff); - _gasreport = _gasreport - gasleft(); - console.log("GAS REPORT: set bytes2 in bytes32 [Bytes.setBytes2(input, 8, 0xffff);]:", _gasreport); - - assertEq(Bytes.setBytes2(input, 0, 0xffff), bytes32(bytes2(0xffff))); - assertEq(Bytes.setBytes2(input, 30, 0xffff), bytes32(uint256(0xffff))); - } - - function testSetBytes4() public { - uint256 _gasreport; - bytes32 input = bytes32(0); - - _gasreport = gasleft(); - Bytes.setBytes4(input, 8, 0xffffffff); - _gasreport = _gasreport - gasleft(); - console.log("GAS REPORT: set bytes4 in bytes32 [Bytes.setBytes4(input, 8, 0xffffffff);]:", _gasreport); - - assertEq(Bytes.setBytes4(input, 0, 0xffffffff), bytes32(bytes4(0xffffffff))); - assertEq(Bytes.setBytes4(input, 30, 0xffffffff), bytes32(uint256(0xffff))); - assertEq(Bytes.setBytes4(input, 28, 0xffffffff), bytes32(uint256(0xffffffff))); - - bytes32 input2 = bytes32(0x0000000a000a0000000000000000000000000000000000000000000000000000); - bytes4 overwrite = bytes4(0x0000006d); - - assertEq( - Bytes.setBytes4(input2, 0, overwrite), - bytes32(0x0000006d000a0000000000000000000000000000000000000000000000000000) - ); - } -} From 3cba3284577970d93d66749e2a36e20698344cfd Mon Sep 17 00:00:00 2001 From: alvrs Date: Wed, 1 Feb 2023 19:14:12 +0100 Subject: [PATCH 48/82] build(store): fix push for gas report action --- .github/workflows/gasreport.yml | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/.github/workflows/gasreport.yml b/.github/workflows/gasreport.yml index aac9d2cea1..e7dda957d2 100644 --- a/.github/workflows/gasreport.yml +++ b/.github/workflows/gasreport.yml @@ -1,5 +1,5 @@ # .github/workflows/gasreport.yml -name: Store gas report +name: Gas report on: pull_request: @@ -7,7 +7,7 @@ on: - packages/store/** jobs: - test: + gas-report: runs-on: ubuntu-22.04 name: Run tests steps: @@ -32,14 +32,9 @@ jobs: - name: Run gas report run: yarn workspace @latticexyz/store run gasreport - - name: Commit updated workflow + - name: Commit and push gas report run: | git config --local user.email "github-actions@github.com" - git config --local user.name "github-actions" + git config --local user.name "${{ env.CI_COMMIT_AUTHOR }}" git commit -a -m "chore(store): update gas report" - - - name: Push changes - uses: ad-m/github-push-action@master - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - branch: ${{ github.ref }} + git push From f49bf75c818b6c9c7eec434278d96779303fb42b Mon Sep 17 00:00:00 2001 From: alvrs Date: Wed, 1 Feb 2023 19:19:28 +0100 Subject: [PATCH 49/82] feat(store): add onDelete hook, rename IOnUpdateHooks to IStoreHooks, make naming consistent --- packages/store/src/IStore.sol | 15 ++++-- packages/store/src/StoreCore.sol | 39 ++++++++------ packages/store/src/StoreSwitch.sol | 8 +++ packages/store/src/StoreView.sol | 8 ++- .../{OnUpdateHookTable.sol => HooksTable.sol} | 4 +- packages/store/src/test/StoreCore.t.sol | 52 ++++++++++++++----- 6 files changed, 87 insertions(+), 39 deletions(-) rename packages/store/src/tables/{OnUpdateHookTable.sol => HooksTable.sol} (94%) diff --git a/packages/store/src/IStore.sol b/packages/store/src/IStore.sol index aef0f7d951..90b1a2b4b6 100644 --- a/packages/store/src/IStore.sol +++ b/packages/store/src/IStore.sol @@ -28,8 +28,11 @@ interface IStore { bytes memory data ) external; - // Register a callback to be called when a record is updated - function registerOnUpdateHook(bytes32 table, IOnUpdateHook onUpdateHook) external; + // Register hooks to be called when a record or field is set or deleted + function registerHooks(bytes32 table, IStoreHooks hooks) external; + + // Set full record (including full dynamic data) + function deleteRecord(bytes32 table, bytes32[] memory key) external; // Get full record (including full array, load table schema from storage) function getRecord(bytes32 table, bytes32[] memory key) external view returns (bytes memory data); @@ -53,17 +56,19 @@ interface IStore { function isStore() external view; } -interface IOnUpdateHook { - function onUpdateRecord( +interface IStoreHooks { + function onSetRecord( bytes32 table, bytes32[] memory key, bytes memory data ) external; - function onUpdateField( + function onSetField( bytes32 table, bytes32[] memory key, uint8 schemaIndex, bytes memory data ) external; + + function onDeleteRecord(bytes32 table, bytes32[] memory key) external; } diff --git a/packages/store/src/StoreCore.sol b/packages/store/src/StoreCore.sol index b3dfbbe500..d6987a469f 100644 --- a/packages/store/src/StoreCore.sol +++ b/packages/store/src/StoreCore.sol @@ -9,8 +9,8 @@ import { console } from "forge-std/console.sol"; import { Schema } from "./Schema.sol"; import { PackedCounter } from "./PackedCounter.sol"; import { Buffer, Buffer_ } from "./Buffer.sol"; -import { OnUpdateHookTable, tableId as OnUpdateHookTableId } from "./tables/OnUpdateHookTable.sol"; -import { IOnUpdateHook } from "./IStore.sol"; +import { HooksTable, tableId as HooksTableId } from "./tables/HooksTable.sol"; +import { IStoreHooks } from "./IStore.sol"; // TODO // - Turn all storage pointer to uint256 for consistency (uint256 is better than bytes32 because it's easier to do arithmetic on) @@ -39,7 +39,7 @@ library StoreCore { * TODO: should we turn the schema table into a "proper table" and register it here? */ function initialize() internal { - registerSchema(OnUpdateHookTableId, OnUpdateHookTable.getSchema()); + registerSchema(HooksTableId, HooksTable.getSchema()); } /************************************************************************ @@ -98,10 +98,10 @@ library StoreCore { ************************************************************************/ /* - * Add a hook to be called when a record is set + * Register hooks to be called when a record or field is set or deleted */ - function registerOnUpdateHook(bytes32 table, IOnUpdateHook onUpdateHook) internal { - OnUpdateHookTable.push(table, address(onUpdateHook)); + function registerHooks(bytes32 table, IStoreHooks hooks) external { + HooksTable.push(table, address(hooks)); } /************************************************************************ @@ -136,11 +136,11 @@ library StoreCore { revert StoreCore_InvalidDataLength(expectedLength, data.length); } - // Call update hooks (before actually modifying the state, so observers have access to the previous state if needed) - address[] memory onUpdateHooks = OnUpdateHookTable.get(table); - for (uint256 i = 0; i < onUpdateHooks.length; i++) { - IOnUpdateHook hook = IOnUpdateHook(onUpdateHooks[i]); - hook.onUpdateRecord(table, key, data); + // Call onSetRecord hooks (before actually modifying the state, so observers have access to the previous state if needed) + address[] memory hooks = HooksTable.get(table); + for (uint256 i = 0; i < hooks.length; i++) { + IStoreHooks hook = IStoreHooks(hooks[i]); + hook.onSetRecord(table, key, data); } // Store the static data at the static data location @@ -181,11 +181,11 @@ library StoreCore { ) internal { Schema schema = getSchema(table); - // Call update hooks (before actually modifying the state, so observers have access to the previous state if needed) - address[] memory onUpdateHooks = OnUpdateHookTable.get(table); - for (uint256 i = 0; i < onUpdateHooks.length; i++) { - IOnUpdateHook hook = IOnUpdateHook(onUpdateHooks[i]); - hook.onUpdateField(table, key, schemaIndex, data); + // Call onSetField hooks (before actually modifying the state, so observers have access to the previous state if needed) + address[] memory hooks = HooksTable.get(table); + for (uint256 i = 0; i < hooks.length; i++) { + IStoreHooks hook = IStoreHooks(hooks[i]); + hook.onSetField(table, key, schemaIndex, data); } if (schemaIndex < schema.numStaticFields()) { @@ -237,6 +237,13 @@ library StoreCore { // Get schema for this table Schema schema = getSchema(table); + // Call onDeleteRecord hooks (before actually modifying the state, so observers have access to the previous state if needed) + address[] memory hooks = HooksTable.get(table); + for (uint256 i = 0; i < hooks.length; i++) { + IStoreHooks hook = IStoreHooks(hooks[i]); + hook.onDeleteRecord(table, key); + } + // Delete static data bytes32 staticDataLocation = _getStaticDataLocation(table, key); Storage.write(staticDataLocation, 0, new bytes(schema.staticDataLength())); diff --git a/packages/store/src/StoreSwitch.sol b/packages/store/src/StoreSwitch.sol index d774ce4eee..cc666b7512 100644 --- a/packages/store/src/StoreSwitch.sol +++ b/packages/store/src/StoreSwitch.sol @@ -74,6 +74,14 @@ library StoreSwitch { } } + function deleteRecord(bytes32 table, bytes32[] memory key) internal { + if (isDelegateCall()) { + StoreCore.deleteRecord(table, key); + } else { + IStore(msg.sender).deleteRecord(table, key); + } + } + function getRecord(bytes32 table, bytes32[] memory key) internal view returns (bytes memory) { if (isDelegateCall()) { return StoreCore.getRecord(table, key); diff --git a/packages/store/src/StoreView.sol b/packages/store/src/StoreView.sol index 1591d12e58..cf0472172d 100644 --- a/packages/store/src/StoreView.sol +++ b/packages/store/src/StoreView.sol @@ -2,7 +2,7 @@ pragma solidity >=0.8.0; import { SchemaType } from "./Types.sol"; -import { IStore, IOnUpdateHook } from "./IStore.sol"; +import { IStore, IStoreHooks } from "./IStore.sol"; import { StoreCore } from "./StoreCore.sol"; import { Schema } from "./Schema.sol"; @@ -40,7 +40,11 @@ contract StoreView is IStore { revert Store_BaseContractNotImplemented(); } - function registerOnUpdateHook(bytes32, IOnUpdateHook) public virtual { + function registerHooks(bytes32, IStoreHooks) public virtual { + revert Store_BaseContractNotImplemented(); + } + + function deleteRecord(bytes32, bytes32[] memory) public virtual { revert Store_BaseContractNotImplemented(); } diff --git a/packages/store/src/tables/OnUpdateHookTable.sol b/packages/store/src/tables/HooksTable.sol similarity index 94% rename from packages/store/src/tables/OnUpdateHookTable.sol rename to packages/store/src/tables/HooksTable.sol index 768f5e3e55..22b5b88be9 100644 --- a/packages/store/src/tables/OnUpdateHookTable.sol +++ b/packages/store/src/tables/HooksTable.sol @@ -12,12 +12,12 @@ import { PackedCounter, PackedCounter_ } from "../PackedCounter.sol"; import { AddressArray, AddressArray_ } from "../schemas/AddressArray.sol"; // -- User defined schema and tableId -- -bytes32 constant tableId = keccak256("mud.store.table.onUpdateHookTable"); +bytes32 constant tableId = keccak256("mud.store.table.hooks"); // -- Autogenerated library to interact with tables with this schema -- // TODO: autogenerate -library OnUpdateHookTable { +library HooksTable { /** Get the table's schema */ function getSchema() internal pure returns (Schema schema) { return AddressArray_.getSchema(); diff --git a/packages/store/src/test/StoreCore.t.sol b/packages/store/src/test/StoreCore.t.sol index 63b8f8c007..298b1e6c02 100644 --- a/packages/store/src/test/StoreCore.t.sol +++ b/packages/store/src/test/StoreCore.t.sol @@ -13,7 +13,7 @@ import { Buffer, Buffer_ } from "../Buffer.sol"; import { Schema, Schema_ } from "../Schema.sol"; import { PackedCounter, PackedCounter_ } from "../PackedCounter.sol"; import { StoreView } from "../StoreView.sol"; -import { IStore, IOnUpdateHook } from "../IStore.sol"; +import { IStore, IStoreHooks } from "../IStore.sol"; import { StoreSwitch } from "../StoreSwitch.sol"; struct TestStruct { @@ -27,7 +27,7 @@ contract StoreCoreTest is Test, StoreView { mapping(uint256 => bytes) private testMapping; - // Expose an external setRecord function for testing purposes of indexers (see testOnUpdateHook) + // Expose an external setRecord function for testing purposes of indexers (see testHooks) function setRecord( bytes32 table, bytes32[] memory key, @@ -36,7 +36,7 @@ contract StoreCoreTest is Test, StoreView { StoreCore.setRecord(table, key, data); } - // Expose an external setField function for testing purposes of indexers (see testOnUpdateHook) + // Expose an external setField function for testing purposes of indexers (see testHooks) function setField( bytes32 table, bytes32[] memory key, @@ -46,7 +46,12 @@ contract StoreCoreTest is Test, StoreView { StoreCore.setField(table, key, schemaIndex, data); } - // Expose an external registerSchema function for testing purposes of indexers (see testOnUpdateHook) + // Expose an external deleteRecord function for testing purposes of indexers (see testHooks) + function deleteRecord(bytes32 table, bytes32[] memory key) public override { + StoreCore.deleteRecord(table, key); + } + + // Expose an external registerSchema function for testing purposes of indexers (see testHooks) function registerSchema(bytes32 table, Schema schema) public override { StoreCore.registerSchema(table, schema); } @@ -469,7 +474,7 @@ contract StoreCoreTest is Test, StoreView { assertEq(data3.length, 0); } - function testOnUpdateHook() public { + function testHooks() public { bytes32 table = keccak256("some.table"); bytes32[] memory key = new bytes32[](1); key[0] = keccak256("some key"); @@ -482,7 +487,7 @@ contract StoreCoreTest is Test, StoreView { MirrorSubscriber subscriber = new MirrorSubscriber(table, schema); // !gasreport register subscriber - StoreCore.registerOnUpdateHook(table, subscriber); + StoreCore.registerHooks(table, subscriber); bytes memory data = bytes.concat(bytes16(0x0102030405060708090a0b0c0d0e0f10)); @@ -495,15 +500,22 @@ contract StoreCoreTest is Test, StoreView { data = bytes.concat(bytes16(0x1112131415161718191a1b1c1d1e1f20)); - // !gasreport set field on table with subscriber + // !gasreport set static field on table with subscriber StoreCore.setField(table, key, 0, data); // Get data from indexed table - the indexer should have mirrored the data there indexedData = StoreCore.getRecord(indexerTableId, key); assertEq(keccak256(data), keccak256(indexedData)); + + // !gasreport delete record on table with subscriber + StoreCore.deleteRecord(table, key); + + // Get data from indexed table - the indexer should have mirrored the data there + indexedData = StoreCore.getRecord(indexerTableId, key); + assertEq(keccak256(indexedData), keccak256(bytes.concat(bytes16(0)))); } - function testOnUpdateHookDynamicData() public { + function testHooksDynamicData() public { bytes32 table = keccak256("some.table"); bytes32[] memory key = new bytes32[](1); key[0] = keccak256("some key"); @@ -516,7 +528,7 @@ contract StoreCoreTest is Test, StoreView { MirrorSubscriber subscriber = new MirrorSubscriber(table, schema); // !gasreport register subscriber - StoreCore.registerOnUpdateHook(table, subscriber); + StoreCore.registerHooks(table, subscriber); uint32[] memory arrayData = new uint32[](1); arrayData[0] = 0x01020304; @@ -526,7 +538,7 @@ contract StoreCoreTest is Test, StoreView { bytes memory staticData = bytes.concat(bytes16(0x0102030405060708090a0b0c0d0e0f10)); bytes memory data = bytes.concat(staticData, dynamicData); - // !gasreport set record on table with subscriber + // !gasreport set (dynamic) record on table with subscriber StoreCore.setRecord(table, key, data); // Get data from indexed table - the indexer should have mirrored the data there @@ -539,18 +551,25 @@ contract StoreCoreTest is Test, StoreView { dynamicData = bytes.concat(encodedArrayDataLength.unwrap(), arrayDataBytes); data = bytes.concat(staticData, dynamicData); - // !gasreport set field on table with subscriber + // !gasreport set (dynamic) field on table with subscriber StoreCore.setField(table, key, 1, arrayDataBytes); // Get data from indexed table - the indexer should have mirrored the data there indexedData = StoreCore.getRecord(indexerTableId, key); assertEq(keccak256(data), keccak256(indexedData)); + + // !gasreport delete (dynamic) record on table with subscriber + StoreCore.deleteRecord(table, key); + + // Get data from indexed table - the indexer should have mirrored the data there + indexedData = StoreCore.getRecord(indexerTableId, key); + assertEq(keccak256(indexedData), keccak256(bytes.concat(bytes16(0)))); } } bytes32 constant indexerTableId = keccak256("indexer.table"); -contract MirrorSubscriber is IOnUpdateHook { +contract MirrorSubscriber is IStoreHooks { bytes32 _table; constructor(bytes32 table, Schema schema) { @@ -558,7 +577,7 @@ contract MirrorSubscriber is IOnUpdateHook { _table = table; } - function onUpdateRecord( + function onSetRecord( bytes32 table, bytes32[] memory key, bytes memory data @@ -567,7 +586,7 @@ contract MirrorSubscriber is IOnUpdateHook { StoreSwitch.setRecord(indexerTableId, key, data); } - function onUpdateField( + function onSetField( bytes32 table, bytes32[] memory key, uint8 schemaIndex, @@ -576,4 +595,9 @@ contract MirrorSubscriber is IOnUpdateHook { if (table != table) revert("invalid table"); StoreSwitch.setField(indexerTableId, key, schemaIndex, data); } + + function onDeleteRecord(bytes32 table, bytes32[] memory key) public { + if (table != table) revert("invalid table"); + StoreSwitch.deleteRecord(indexerTableId, key); + } } From 373f68c50fceb7e5cc09e022636771c0e4adbf03 Mon Sep 17 00:00:00 2001 From: alvrs Date: Wed, 1 Feb 2023 19:30:47 +0100 Subject: [PATCH 50/82] build(store): add explicit commit author for gas report action --- .github/workflows/gasreport.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gasreport.yml b/.github/workflows/gasreport.yml index e7dda957d2..25c991e874 100644 --- a/.github/workflows/gasreport.yml +++ b/.github/workflows/gasreport.yml @@ -35,6 +35,6 @@ jobs: - name: Commit and push gas report run: | git config --local user.email "github-actions@github.com" - git config --local user.name "${{ env.CI_COMMIT_AUTHOR }}" + git config --local user.name "Gas report" git commit -a -m "chore(store): update gas report" git push From 8096ef0702c2172bf7eb8b8d3685ebf6da7a967b Mon Sep 17 00:00:00 2001 From: alvrs Date: Wed, 1 Feb 2023 19:34:57 +0100 Subject: [PATCH 51/82] refactor(store): prefix store events with Mud --- packages/store/src/IStore.sol | 6 +++--- packages/store/src/StoreCore.sol | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/store/src/IStore.sol b/packages/store/src/IStore.sol index 90b1a2b4b6..040fa0814d 100644 --- a/packages/store/src/IStore.sol +++ b/packages/store/src/IStore.sol @@ -5,9 +5,9 @@ import { SchemaType } from "./Types.sol"; import { Schema } from "./Schema.sol"; interface IStore { - event StoreSetRecord(bytes32 table, bytes32[] key, bytes data); - event StoreSetField(bytes32 table, bytes32[] key, uint8 schemaIndex, bytes data); - event StoreDeleteRecord(bytes32 table, bytes32[] key); + event MudStoreSetRecord(bytes32 table, bytes32[] key, bytes data); + event MudStoreSetField(bytes32 table, bytes32[] key, uint8 schemaIndex, bytes data); + event MudStoreDeleteRecord(bytes32 table, bytes32[] key); function registerSchema(bytes32 table, Schema schema) external; diff --git a/packages/store/src/StoreCore.sol b/packages/store/src/StoreCore.sol index d6987a469f..1c623e6601 100644 --- a/packages/store/src/StoreCore.sol +++ b/packages/store/src/StoreCore.sol @@ -20,9 +20,9 @@ import { IStoreHooks } from "./IStore.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 table, bytes32[] key, bytes data); - event StoreSetField(bytes32 table, bytes32[] key, uint8 schemaIndex, bytes data); - event StoreDeleteRecord(bytes32 table, bytes32[] key); + event MudStoreSetRecord(bytes32 table, bytes32[] key, bytes data); + event MudStoreSetField(bytes32 table, bytes32[] key, uint8 schemaIndex, bytes data); + event MudStoreDeleteRecord(bytes32 table, bytes32[] key); bytes32 internal constant SLOT = keccak256("mud.store"); bytes32 internal constant SCHEMA_TABLE = keccak256("mud.store.table.schema"); @@ -170,7 +170,7 @@ library StoreCore { } // Emit event to notify indexers - emit StoreSetRecord(table, key, data); + emit MudStoreSetRecord(table, key, data); } function setField( @@ -195,7 +195,7 @@ library StoreCore { } // Emit event to notify indexers - emit StoreSetField(table, key, schemaIndex, data); + emit MudStoreSetField(table, key, schemaIndex, data); } function _setStaticField( @@ -267,7 +267,7 @@ library StoreCore { Storage.write(dynamicDataLengthLocation, bytes32(0)); // Emit event to notify indexers - emit StoreDeleteRecord(table, key); + emit MudStoreDeleteRecord(table, key); } /************************************************************************ From 92ae909f329a40ccc18e9213caa026aab557ec25 Mon Sep 17 00:00:00 2001 From: alvrs Date: Wed, 1 Feb 2023 20:03:02 +0100 Subject: [PATCH 52/82] build: fix gasreport detached head --- .github/workflows/gasreport.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/gasreport.yml b/.github/workflows/gasreport.yml index 25c991e874..278c922960 100644 --- a/.github/workflows/gasreport.yml +++ b/.github/workflows/gasreport.yml @@ -36,5 +36,6 @@ jobs: run: | git config --local user.email "github-actions@github.com" git config --local user.name "Gas report" + git checkout "${GITHUB_HEAD_REF}" git commit -a -m "chore(store): update gas report" git push From 42a844ab57d4b0cedcadcd13f2a3aef093e3a576 Mon Sep 17 00:00:00 2001 From: alvrs Date: Wed, 1 Feb 2023 20:05:35 +0100 Subject: [PATCH 53/82] refactor(store): rename IStoreHooks to IStoreHook --- packages/store/src/IStore.sol | 4 ++-- packages/store/src/StoreCore.sol | 10 +++++----- packages/store/src/StoreView.sol | 4 ++-- packages/store/src/test/StoreCore.t.sol | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/store/src/IStore.sol b/packages/store/src/IStore.sol index 040fa0814d..da3b83c806 100644 --- a/packages/store/src/IStore.sol +++ b/packages/store/src/IStore.sol @@ -29,7 +29,7 @@ interface IStore { ) external; // Register hooks to be called when a record or field is set or deleted - function registerHooks(bytes32 table, IStoreHooks hooks) external; + function registerHooks(bytes32 table, IStoreHook hooks) external; // Set full record (including full dynamic data) function deleteRecord(bytes32 table, bytes32[] memory key) external; @@ -56,7 +56,7 @@ interface IStore { function isStore() external view; } -interface IStoreHooks { +interface IStoreHook { function onSetRecord( bytes32 table, bytes32[] memory key, diff --git a/packages/store/src/StoreCore.sol b/packages/store/src/StoreCore.sol index 1c623e6601..784ef46dc3 100644 --- a/packages/store/src/StoreCore.sol +++ b/packages/store/src/StoreCore.sol @@ -10,7 +10,7 @@ import { Schema } from "./Schema.sol"; import { PackedCounter } from "./PackedCounter.sol"; import { Buffer, Buffer_ } from "./Buffer.sol"; import { HooksTable, tableId as HooksTableId } from "./tables/HooksTable.sol"; -import { IStoreHooks } from "./IStore.sol"; +import { IStoreHook } from "./IStore.sol"; // TODO // - Turn all storage pointer to uint256 for consistency (uint256 is better than bytes32 because it's easier to do arithmetic on) @@ -100,7 +100,7 @@ library StoreCore { /* * Register hooks to be called when a record or field is set or deleted */ - function registerHooks(bytes32 table, IStoreHooks hooks) external { + function registerHooks(bytes32 table, IStoreHook hooks) external { HooksTable.push(table, address(hooks)); } @@ -139,7 +139,7 @@ library StoreCore { // Call onSetRecord hooks (before actually modifying the state, so observers have access to the previous state if needed) address[] memory hooks = HooksTable.get(table); for (uint256 i = 0; i < hooks.length; i++) { - IStoreHooks hook = IStoreHooks(hooks[i]); + IStoreHook hook = IStoreHook(hooks[i]); hook.onSetRecord(table, key, data); } @@ -184,7 +184,7 @@ library StoreCore { // Call onSetField hooks (before actually modifying the state, so observers have access to the previous state if needed) address[] memory hooks = HooksTable.get(table); for (uint256 i = 0; i < hooks.length; i++) { - IStoreHooks hook = IStoreHooks(hooks[i]); + IStoreHook hook = IStoreHook(hooks[i]); hook.onSetField(table, key, schemaIndex, data); } @@ -240,7 +240,7 @@ library StoreCore { // Call onDeleteRecord hooks (before actually modifying the state, so observers have access to the previous state if needed) address[] memory hooks = HooksTable.get(table); for (uint256 i = 0; i < hooks.length; i++) { - IStoreHooks hook = IStoreHooks(hooks[i]); + IStoreHook hook = IStoreHook(hooks[i]); hook.onDeleteRecord(table, key); } diff --git a/packages/store/src/StoreView.sol b/packages/store/src/StoreView.sol index cf0472172d..9940a438e4 100644 --- a/packages/store/src/StoreView.sol +++ b/packages/store/src/StoreView.sol @@ -2,7 +2,7 @@ pragma solidity >=0.8.0; import { SchemaType } from "./Types.sol"; -import { IStore, IStoreHooks } from "./IStore.sol"; +import { IStore, IStoreHook } from "./IStore.sol"; import { StoreCore } from "./StoreCore.sol"; import { Schema } from "./Schema.sol"; @@ -40,7 +40,7 @@ contract StoreView is IStore { revert Store_BaseContractNotImplemented(); } - function registerHooks(bytes32, IStoreHooks) public virtual { + function registerHooks(bytes32, IStoreHook) public virtual { revert Store_BaseContractNotImplemented(); } diff --git a/packages/store/src/test/StoreCore.t.sol b/packages/store/src/test/StoreCore.t.sol index 298b1e6c02..a7e1b9e46a 100644 --- a/packages/store/src/test/StoreCore.t.sol +++ b/packages/store/src/test/StoreCore.t.sol @@ -13,7 +13,7 @@ import { Buffer, Buffer_ } from "../Buffer.sol"; import { Schema, Schema_ } from "../Schema.sol"; import { PackedCounter, PackedCounter_ } from "../PackedCounter.sol"; import { StoreView } from "../StoreView.sol"; -import { IStore, IStoreHooks } from "../IStore.sol"; +import { IStore, IStoreHook } from "../IStore.sol"; import { StoreSwitch } from "../StoreSwitch.sol"; struct TestStruct { @@ -569,7 +569,7 @@ contract StoreCoreTest is Test, StoreView { bytes32 constant indexerTableId = keccak256("indexer.table"); -contract MirrorSubscriber is IStoreHooks { +contract MirrorSubscriber is IStoreHook { bytes32 _table; constructor(bytes32 table, Schema schema) { From 319ccc73cb62020944e6d56169bd4911ecf10a36 Mon Sep 17 00:00:00 2001 From: alvrs Date: Wed, 1 Feb 2023 20:09:20 +0100 Subject: [PATCH 54/82] refactor(store): rename Schema_ to SchemaLib, PackedCounter_ to PackedCounterLib --- packages/store/gas-report.txt | 4 +-- packages/store/src/PackedCounter.sol | 4 +-- packages/store/src/Schema.sol | 28 +++++++-------- packages/store/src/schemas/AddressArray.sol | 6 ++-- packages/store/src/schemas/CallbackArray.sol | 6 ++-- packages/store/src/schemas/Mixed.sol | 8 ++--- packages/store/src/schemas/Route.sol | 4 +-- packages/store/src/schemas/Vector2.sol | 4 +-- packages/store/src/tables/HooksTable.sol | 4 +-- packages/store/src/tables/MixedTable.sol | 4 +-- packages/store/src/tables/RouteTable.sol | 2 +- packages/store/src/tables/Vector2Table.sol | 2 +- packages/store/src/test/PackedCounter.t.sol | 8 ++--- packages/store/src/test/Schema.t.sol | 28 +++++++-------- packages/store/src/test/StoreCore.t.sol | 36 ++++++++++---------- 15 files changed, 74 insertions(+), 74 deletions(-) diff --git a/packages/store/gas-report.txt b/packages/store/gas-report.txt index b43caa2949..1bd5126764 100644 --- a/packages/store/gas-report.txt +++ b/packages/store/gas-report.txt @@ -59,12 +59,12 @@ (src/test/MixedTable.t.sol) | get record from MixedTable [Mixed memory mixed = MixedTable.get(key)]: 20494 (src/test/PackedCounter.t.sol) | get value at index of PackedCounter [packedCounter.atIndex(3)]: 272 (src/test/PackedCounter.t.sol) | set value at index of PackedCounter [packedCounter = packedCounter.setAtIndex(2, 5)]: 884 -(src/test/PackedCounter.t.sol) | pack uint16 array into PackedCounter [PackedCounter packedCounter = PackedCounter_.pack(counters)]: 2148 +(src/test/PackedCounter.t.sol) | pack uint16 array into PackedCounter [PackedCounter packedCounter = PackedCounterLib.pack(counters)]: 2148 (src/test/PackedCounter.t.sol) | get total of PackedCounter [packedCounter.total()]: 33 (src/test/RouteTable.t.sol) | register RouteTable schema [RouteTable.registerSchema()]: 30440 (src/test/RouteTable.t.sol) | set RouteTable record [RouteTable.set(key, addr, selector, executionMode)]: 29726 (src/test/RouteTable.t.sol) | get RouteTable record [Route memory systemEntry = RouteTable.get(key)]: 6506 -(src/test/Schema.t.sol) | encode schema with 6 entries [Schema_.encode]: 6202 +(src/test/Schema.t.sol) | encode schema with 6 entries [SchemaLib.encode]: 6202 (src/test/Schema.t.sol) | get schema type at index [SchemaType schemaType1 = schema.atIndex(0)]: 200 (src/test/Schema.t.sol) | get number of dynamic fields from schema [uint256 num = schema.numDynamicFields()]: 80 (src/test/Schema.t.sol) | get number of static fields from schema [uint256 num = schema.numStaticFields()]: 91 diff --git a/packages/store/src/PackedCounter.sol b/packages/store/src/PackedCounter.sol index cd24a054b1..60eb706c28 100644 --- a/packages/store/src/PackedCounter.sol +++ b/packages/store/src/PackedCounter.sol @@ -8,9 +8,9 @@ import { Bytes } from "./Bytes.sol"; // - 2 bytes length per counter type PackedCounter is bytes32; -using PackedCounter_ for PackedCounter global; +using PackedCounterLib for PackedCounter global; -library PackedCounter_ { +library PackedCounterLib { /************************************************************************ * * STATIC FUNCTIONS diff --git a/packages/store/src/Schema.sol b/packages/store/src/Schema.sol index 30f663cd5b..3ba1b5f79a 100644 --- a/packages/store/src/Schema.sol +++ b/packages/store/src/Schema.sol @@ -10,11 +10,11 @@ import { Bytes } from "./Bytes.sol"; // - 28 bytes for 28 schema types (max 14 dynamic fields to we can pack their lengths into 1 word) type Schema is bytes32; -using Schema_ for Schema global; +using SchemaLib for Schema global; -library Schema_ { - error Schema_InvalidLength(uint256 length); - error Schema_StaticTypeAfterDynamicType(); +library SchemaLib { + error SchemaLibInvalidLength(uint256 length); + error SchemaLibStaticTypeAfterDynamicType(); /************************************************************************ * @@ -26,7 +26,7 @@ library Schema_ { * Encode the given schema into a single bytes32 */ function encode(SchemaType[] memory _schema) internal pure returns (Schema) { - if (_schema.length > 28) revert Schema_InvalidLength(_schema.length); + if (_schema.length > 28) revert SchemaLibInvalidLength(_schema.length); bytes32 schema; uint16 length; uint8 staticFields; @@ -40,7 +40,7 @@ library Schema_ { // Increase the static field count if the field is static if (staticByteLength > 0) { // Revert if we have seen a dynamic field before, but now we see a static field - if (hasDynamicFields) revert Schema_StaticTypeAfterDynamicType(); + if (hasDynamicFields) revert SchemaLibStaticTypeAfterDynamicType(); staticFields++; } else { // Flag that we have seen a dynamic field @@ -56,7 +56,7 @@ library Schema_ { // Require max 14 dynamic fields uint8 dynamicFields = uint8(_schema.length) - staticFields; - if (dynamicFields > 14) revert Schema_InvalidLength(dynamicFields); + if (dynamicFields > 14) revert SchemaLibInvalidLength(dynamicFields); // Store total static length, and number of static and dynamic fields schema = Bytes.setBytes1(schema, 0, bytes1(bytes2(length))); // upper length byte @@ -184,15 +184,15 @@ library Schema_ { function validate(Schema schema) internal view { // Schema must not be empty - if (schema.isEmpty()) revert Schema_InvalidLength(0); + if (schema.isEmpty()) revert SchemaLibInvalidLength(0); // Schema must have no more than 14 dynamic fields uint256 _numDynamicFields = schema.numDynamicFields(); - if (_numDynamicFields > 14) revert Schema_InvalidLength(_numDynamicFields); + if (_numDynamicFields > 14) revert SchemaLibInvalidLength(_numDynamicFields); uint256 _numStaticFields = schema.numStaticFields(); // Schema must not have more than 28 fields in total - if (_numStaticFields + _numDynamicFields > 28) revert Schema_InvalidLength(_numStaticFields + _numDynamicFields); + if (_numStaticFields + _numDynamicFields > 28) revert SchemaLibInvalidLength(_numStaticFields + _numDynamicFields); // No static field can be after a dynamic field uint256 countStaticFields; @@ -200,11 +200,11 @@ library Schema_ { for (uint256 i; i < _numStaticFields + _numDynamicFields; ) { if (getStaticByteLength(schema.atIndex(i)) > 0) { // Static field in dynamic part - if (i >= _numStaticFields) revert Schema_StaticTypeAfterDynamicType(); + if (i >= _numStaticFields) revert SchemaLibStaticTypeAfterDynamicType(); countStaticFields++; } else { // Dynamic field in static part - if (i < _numStaticFields) revert Schema_StaticTypeAfterDynamicType(); + if (i < _numStaticFields) revert SchemaLibStaticTypeAfterDynamicType(); countDynamicFields++; } unchecked { @@ -213,10 +213,10 @@ library Schema_ { } // Number of static fields must match - if (countStaticFields != _numStaticFields) revert Schema_InvalidLength(countStaticFields); + if (countStaticFields != _numStaticFields) revert SchemaLibInvalidLength(countStaticFields); // Number of dynamic fields must match - if (countDynamicFields != _numDynamicFields) revert Schema_InvalidLength(countDynamicFields); + if (countDynamicFields != _numDynamicFields) revert SchemaLibInvalidLength(countDynamicFields); } /** diff --git a/packages/store/src/schemas/AddressArray.sol b/packages/store/src/schemas/AddressArray.sol index 5a73191e72..a7e5c8b87b 100644 --- a/packages/store/src/schemas/AddressArray.sol +++ b/packages/store/src/schemas/AddressArray.sol @@ -7,8 +7,8 @@ import { StoreSwitch } from "../StoreSwitch.sol"; import { StoreCore } from "../StoreCore.sol"; import { SchemaType } from "../Types.sol"; import { Bytes } from "../Bytes.sol"; -import { Schema, Schema_ } from "../Schema.sol"; -import { PackedCounter, PackedCounter_ } from "../PackedCounter.sol"; +import { Schema, SchemaLib } from "../Schema.sol"; +import { PackedCounter, PackedCounterLib } from "../PackedCounter.sol"; // -- User defined schema and tableId -- struct AddressArray { @@ -21,7 +21,7 @@ struct AddressArray { library AddressArray_ { /** Get the table's schema */ function getSchema() internal pure returns (Schema schema) { - schema = Schema_.encode(SchemaType.AddressArray); + schema = SchemaLib.encode(SchemaType.AddressArray); } /** Register the table's schema */ diff --git a/packages/store/src/schemas/CallbackArray.sol b/packages/store/src/schemas/CallbackArray.sol index 1441cd9473..a6575f0235 100644 --- a/packages/store/src/schemas/CallbackArray.sol +++ b/packages/store/src/schemas/CallbackArray.sol @@ -7,8 +7,8 @@ import { StoreSwitch } from "../StoreSwitch.sol"; import { StoreCore } from "../StoreCore.sol"; import { SchemaType } from "../Types.sol"; import { Bytes } from "../Bytes.sol"; -import { Schema, Schema_ } from "../Schema.sol"; -import { PackedCounter, PackedCounter_ } from "../PackedCounter.sol"; +import { Schema, SchemaLib } from "../Schema.sol"; +import { PackedCounter, PackedCounterLib } from "../PackedCounter.sol"; // -- User defined schema and tableId -- struct CallbackArray { @@ -21,7 +21,7 @@ struct CallbackArray { library CallbackArray_ { /** Get the table's schema */ function getSchema() internal pure returns (Schema schema) { - schema = Schema_.encode(SchemaType.Bytes24Array); + schema = SchemaLib.encode(SchemaType.Bytes24Array); } /** Register the table's schema */ diff --git a/packages/store/src/schemas/Mixed.sol b/packages/store/src/schemas/Mixed.sol index 0c21bc4115..1b7ed0d8be 100644 --- a/packages/store/src/schemas/Mixed.sol +++ b/packages/store/src/schemas/Mixed.sol @@ -7,8 +7,8 @@ import { StoreSwitch } from "../StoreSwitch.sol"; import { StoreCore } from "../StoreCore.sol"; import { SchemaType } from "../Types.sol"; import { Bytes } from "../Bytes.sol"; -import { Schema, Schema_ } from "../Schema.sol"; -import { PackedCounter, PackedCounter_ } from "../PackedCounter.sol"; +import { Schema, SchemaLib } from "../Schema.sol"; +import { PackedCounter, PackedCounterLib } from "../PackedCounter.sol"; // -- User defined schema -- @@ -25,7 +25,7 @@ struct Mixed { library Mixed_ { /** Get the table's schema */ function getSchema() internal pure returns (Schema schema) { - schema = Schema_.encode(SchemaType.Uint32, SchemaType.Uint128, SchemaType.Uint32Array, SchemaType.String); + schema = SchemaLib.encode(SchemaType.Uint32, SchemaType.Uint128, SchemaType.Uint32Array, SchemaType.String); } /** Register the table's schema */ @@ -46,7 +46,7 @@ library Mixed_ { uint32[] memory a32, string memory s ) internal { - PackedCounter encodedLengths = PackedCounter_.pack(uint16(a32.length * 4), uint16(Bytes.from(s).length)); + PackedCounter encodedLengths = PackedCounterLib.pack(uint16(a32.length * 4), uint16(Bytes.from(s).length)); bytes memory data = bytes.concat( bytes4(u32), diff --git a/packages/store/src/schemas/Route.sol b/packages/store/src/schemas/Route.sol index d33c75ae72..8d49075a65 100644 --- a/packages/store/src/schemas/Route.sol +++ b/packages/store/src/schemas/Route.sol @@ -7,7 +7,7 @@ import { StoreSwitch } from "../StoreSwitch.sol"; import { StoreCore } from "../StoreCore.sol"; import { SchemaType } from "../Types.sol"; import { Bytes } from "../Bytes.sol"; -import { Schema, Schema_ } from "../Schema.sol"; +import { Schema, SchemaLib } from "../Schema.sol"; // -- User defined schema -- @@ -23,7 +23,7 @@ struct Route { library Route_ { /** Get the table's schema */ function getSchema() internal pure returns (Schema schema) { - schema = Schema_.encode(SchemaType.Address, SchemaType.Bytes4, SchemaType.Uint8); + schema = SchemaLib.encode(SchemaType.Address, SchemaType.Bytes4, SchemaType.Uint8); } /** Register the table's schema */ diff --git a/packages/store/src/schemas/Vector2.sol b/packages/store/src/schemas/Vector2.sol index 8f8d9b2a39..b3912e647e 100644 --- a/packages/store/src/schemas/Vector2.sol +++ b/packages/store/src/schemas/Vector2.sol @@ -7,7 +7,7 @@ import { StoreSwitch } from "../StoreSwitch.sol"; import { StoreCore } from "../StoreCore.sol"; import { SchemaType } from "../Types.sol"; import { Bytes } from "../Bytes.sol"; -import { Schema, Schema_ } from "../Schema.sol"; +import { Schema, SchemaLib } from "../Schema.sol"; // -- User defined schema -- @@ -22,7 +22,7 @@ struct Vector2 { library Vector2_ { /** Get the table's schema */ function getSchema() internal pure returns (Schema schema) { - schema = Schema_.encode(SchemaType.Uint32, SchemaType.Uint32); + schema = SchemaLib.encode(SchemaType.Uint32, SchemaType.Uint32); } /** Register the table's schema */ diff --git a/packages/store/src/tables/HooksTable.sol b/packages/store/src/tables/HooksTable.sol index 22b5b88be9..8926ebc1b4 100644 --- a/packages/store/src/tables/HooksTable.sol +++ b/packages/store/src/tables/HooksTable.sol @@ -7,8 +7,8 @@ import { StoreSwitch } from "../StoreSwitch.sol"; import { StoreCore } from "../StoreCore.sol"; import { SchemaType } from "../Types.sol"; import { Bytes } from "../Bytes.sol"; -import { Schema, Schema_ } from "../Schema.sol"; -import { PackedCounter, PackedCounter_ } from "../PackedCounter.sol"; +import { Schema, SchemaLib } from "../Schema.sol"; +import { PackedCounter, PackedCounterLib } from "../PackedCounter.sol"; import { AddressArray, AddressArray_ } from "../schemas/AddressArray.sol"; // -- User defined schema and tableId -- diff --git a/packages/store/src/tables/MixedTable.sol b/packages/store/src/tables/MixedTable.sol index 76dc7c91b6..a420b2893b 100644 --- a/packages/store/src/tables/MixedTable.sol +++ b/packages/store/src/tables/MixedTable.sol @@ -7,8 +7,8 @@ import { StoreSwitch } from "../StoreSwitch.sol"; import { StoreCore } from "../StoreCore.sol"; import { SchemaType } from "../Types.sol"; import { Bytes } from "../Bytes.sol"; -import { Schema, Schema_ } from "../Schema.sol"; -import { PackedCounter, PackedCounter_ } from "../PackedCounter.sol"; +import { Schema, SchemaLib } from "../Schema.sol"; +import { PackedCounter, PackedCounterLib } from "../PackedCounter.sol"; import { Mixed, Mixed_ } from "../schemas/Mixed.sol"; // -- User defined tableId -- diff --git a/packages/store/src/tables/RouteTable.sol b/packages/store/src/tables/RouteTable.sol index 124b599e4c..39c79dda30 100644 --- a/packages/store/src/tables/RouteTable.sol +++ b/packages/store/src/tables/RouteTable.sol @@ -7,7 +7,7 @@ import { StoreSwitch } from "../StoreSwitch.sol"; import { StoreCore } from "../StoreCore.sol"; import { SchemaType } from "../Types.sol"; import { Bytes } from "../Bytes.sol"; -import { Schema, Schema_ } from "../Schema.sol"; +import { Schema, SchemaLib } from "../Schema.sol"; import { Route, Route_ } from "../schemas/Route.sol"; // -- User defined tableId -- diff --git a/packages/store/src/tables/Vector2Table.sol b/packages/store/src/tables/Vector2Table.sol index 91ae6ef4ee..15fcdf7c24 100644 --- a/packages/store/src/tables/Vector2Table.sol +++ b/packages/store/src/tables/Vector2Table.sol @@ -7,7 +7,7 @@ import { StoreSwitch } from "../StoreSwitch.sol"; import { StoreCore } from "../StoreCore.sol"; import { SchemaType } from "../Types.sol"; import { Bytes } from "../Bytes.sol"; -import { Schema, Schema_ } from "../Schema.sol"; +import { Schema, SchemaLib } from "../Schema.sol"; import { Vector2, Vector2_ } from "../schemas/Vector2.sol"; // -- User defined tableId -- diff --git a/packages/store/src/test/PackedCounter.t.sol b/packages/store/src/test/PackedCounter.t.sol index ab11e5cdcb..9aeb468f6d 100644 --- a/packages/store/src/test/PackedCounter.t.sol +++ b/packages/store/src/test/PackedCounter.t.sol @@ -2,7 +2,7 @@ pragma solidity >=0.8.0; import "forge-std/Test.sol"; -import { PackedCounter, PackedCounter_ } from "../PackedCounter.sol"; +import { PackedCounter, PackedCounterLib } from "../PackedCounter.sol"; contract PackedCounterTest is Test { function testTotal() public { @@ -13,7 +13,7 @@ contract PackedCounterTest is Test { counters[3] = 4; // !gasreport pack uint16 array into PackedCounter - PackedCounter packedCounter = PackedCounter_.pack(counters); + PackedCounter packedCounter = PackedCounterLib.pack(counters); // !gasreport get total of PackedCounter packedCounter.total(); @@ -28,7 +28,7 @@ contract PackedCounterTest is Test { counters[2] = 3; counters[3] = 4; - PackedCounter packedCounter = PackedCounter_.pack(counters); + PackedCounter packedCounter = PackedCounterLib.pack(counters); // !gasreport get value at index of PackedCounter packedCounter.atIndex(3); @@ -46,7 +46,7 @@ contract PackedCounterTest is Test { counters[2] = 3; counters[3] = 4; - PackedCounter packedCounter = PackedCounter_.pack(counters); + PackedCounter packedCounter = PackedCounterLib.pack(counters); // !gasreport set value at index of PackedCounter packedCounter = packedCounter.setAtIndex(2, 5); diff --git a/packages/store/src/test/Schema.t.sol b/packages/store/src/test/Schema.t.sol index a18b0b6056..f63feb446c 100644 --- a/packages/store/src/test/Schema.t.sol +++ b/packages/store/src/test/Schema.t.sol @@ -2,13 +2,13 @@ pragma solidity >=0.8.0; import "forge-std/Test.sol"; -import { Schema, Schema_ } from "../Schema.sol"; +import { Schema, SchemaLib } from "../Schema.sol"; import { SchemaType } from "../Types.sol"; contract SchemaTest is Test { function testEncodeDecodeSchema() public { uint256 gas = gasleft(); - Schema schema = Schema_.encode( + Schema schema = SchemaLib.encode( SchemaType.Uint8, // 1 byte SchemaType.Uint16, // 2 bytes SchemaType.Uint32, // 4 bytes @@ -17,7 +17,7 @@ contract SchemaTest is Test { SchemaType.Uint32Array // 0 bytes (because it's dynamic) ); gas = gas - gasleft(); - console.log("GAS REPORT: encode schema with 6 entries [Schema_.encode]: %s", gas); + console.log("GAS REPORT: encode schema with 6 entries [SchemaLib.encode]: %s", gas); // !gasreport get schema type at index SchemaType schemaType1 = schema.atIndex(0); @@ -31,7 +31,7 @@ contract SchemaTest is Test { } function testFailInvalidSchemaStaticAfterDynamic() public pure { - Schema_.encode(SchemaType.Uint8, SchemaType.Uint32Array, SchemaType.Uint16); + SchemaLib.encode(SchemaType.Uint8, SchemaType.Uint32Array, SchemaType.Uint16); } function testEncodeMaxValidLength() public { @@ -64,7 +64,7 @@ contract SchemaTest is Test { schema[25] = SchemaType.Uint32Array; schema[26] = SchemaType.Uint32Array; schema[27] = SchemaType.Uint32Array; - Schema encodedSchema = Schema_.encode(schema); + Schema encodedSchema = SchemaLib.encode(schema); assertEq(encodedSchema.numStaticFields() + encodedSchema.numDynamicFields(), 28); } @@ -100,7 +100,7 @@ contract SchemaTest is Test { schema[26] = SchemaType.Uint32Array; schema[27] = SchemaType.Uint32Array; schema[28] = SchemaType.Uint32Array; - Schema_.encode(schema); + SchemaLib.encode(schema); } function testEncodeMaxValidDynamic() public { @@ -119,7 +119,7 @@ contract SchemaTest is Test { schema[11] = SchemaType.Uint32Array; schema[12] = SchemaType.Uint32Array; schema[13] = SchemaType.Uint32Array; - Schema encodedSchema = Schema_.encode(schema); + Schema encodedSchema = SchemaLib.encode(schema); assertEq(encodedSchema.numDynamicFields(), 14); } @@ -141,11 +141,11 @@ contract SchemaTest is Test { schema[12] = SchemaType.Uint32Array; schema[13] = SchemaType.Uint32Array; schema[14] = SchemaType.Uint32Array; - Schema_.encode(schema); + SchemaLib.encode(schema); } function testGetStaticSchemaLength() public { - Schema schema = Schema_.encode( + Schema schema = SchemaLib.encode( SchemaType.Uint8, // 1 byte SchemaType.Uint16, // 2 bytes SchemaType.Uint32, // 4 bytes @@ -161,7 +161,7 @@ contract SchemaTest is Test { } function testGetNumStaticFields() public { - Schema schema = Schema_.encode( + Schema schema = SchemaLib.encode( SchemaType.Uint8, // 1 byte SchemaType.Uint16, // 2 bytes SchemaType.Uint32, // 4 bytes @@ -177,7 +177,7 @@ contract SchemaTest is Test { } function testGetNumDynamicFields() public { - Schema schema = Schema_.encode( + Schema schema = SchemaLib.encode( SchemaType.Uint8, // 1 byte SchemaType.Uint16, // 2 bytes SchemaType.Uint32, // 4 bytes @@ -222,7 +222,7 @@ contract SchemaTest is Test { schema[25] = SchemaType.Uint32Array; schema[26] = SchemaType.Uint32Array; schema[27] = SchemaType.Uint32Array; - Schema encodedSchema = Schema_.encode(schema); + Schema encodedSchema = SchemaLib.encode(schema); // !gasreport validate schema encodedSchema.validate(); @@ -234,7 +234,7 @@ contract SchemaTest is Test { function testIsEmptyTrue() public { SchemaType[] memory schema = new SchemaType[](0); - Schema encodedSchema = Schema_.encode(schema); + Schema encodedSchema = SchemaLib.encode(schema); // !gasreport check if schema is empty (empty schema) bool empty = encodedSchema.isEmpty(); @@ -243,7 +243,7 @@ contract SchemaTest is Test { } function testIsEmptyFalse() public { - Schema encodedSchema = Schema_.encode(SchemaType.Uint256); + Schema encodedSchema = SchemaLib.encode(SchemaType.Uint256); // !gasreport check if schema is empty (non-empty schema) bool empty = encodedSchema.isEmpty(); diff --git a/packages/store/src/test/StoreCore.t.sol b/packages/store/src/test/StoreCore.t.sol index a7e1b9e46a..4cef44b131 100644 --- a/packages/store/src/test/StoreCore.t.sol +++ b/packages/store/src/test/StoreCore.t.sol @@ -10,8 +10,8 @@ import { Storage } from "../Storage.sol"; import { Memory } from "../Memory.sol"; import { Cast } from "../Cast.sol"; import { Buffer, Buffer_ } from "../Buffer.sol"; -import { Schema, Schema_ } from "../Schema.sol"; -import { PackedCounter, PackedCounter_ } from "../PackedCounter.sol"; +import { Schema, SchemaLib } from "../Schema.sol"; +import { PackedCounter, PackedCounterLib } from "../PackedCounter.sol"; import { StoreView } from "../StoreView.sol"; import { IStore, IStoreHook } from "../IStore.sol"; import { StoreSwitch } from "../StoreSwitch.sol"; @@ -57,7 +57,7 @@ contract StoreCoreTest is Test, StoreView { } function testRegisterAndGetSchema() public { - Schema schema = Schema_.encode(SchemaType.Uint8, SchemaType.Uint16, SchemaType.Uint8, SchemaType.Uint16); + Schema schema = SchemaLib.encode(SchemaType.Uint8, SchemaType.Uint16, SchemaType.Uint8, SchemaType.Uint16); bytes32 table = keccak256("some.table"); // !gasreport StoreCore: register schema @@ -74,7 +74,7 @@ contract StoreCoreTest is Test, StoreView { } function testHasSchema() public { - Schema schema = Schema_.encode(SchemaType.Uint8, SchemaType.Uint16, SchemaType.Uint8, SchemaType.Uint16); + Schema schema = SchemaLib.encode(SchemaType.Uint8, SchemaType.Uint16, SchemaType.Uint8, SchemaType.Uint16); bytes32 table = keccak256("some.table"); bytes32 table2 = keccak256("other.table"); StoreCore.registerSchema(table, schema); @@ -92,7 +92,7 @@ contract StoreCoreTest is Test, StoreView { function testSetAndGetDynamicDataLength() public { bytes32 table = keccak256("some.table"); - Schema schema = Schema_.encode( + Schema schema = SchemaLib.encode( SchemaType.Uint8, SchemaType.Uint16, SchemaType.Uint32, @@ -137,7 +137,7 @@ contract StoreCoreTest is Test, StoreView { function testSetAndGetStaticData() public { // Register table's schema - Schema schema = Schema_.encode(SchemaType.Uint8, SchemaType.Uint16, SchemaType.Uint8, SchemaType.Uint16); + Schema schema = SchemaLib.encode(SchemaType.Uint8, SchemaType.Uint16, SchemaType.Uint8, SchemaType.Uint16); bytes32 table = keccak256("some.table"); StoreCore.registerSchema(table, schema); @@ -160,7 +160,7 @@ contract StoreCoreTest is Test, StoreView { function testFailSetAndGetStaticData() public { // Register table's schema - Schema schema = Schema_.encode(SchemaType.Uint8, SchemaType.Uint16, SchemaType.Uint8, SchemaType.Uint16); + Schema schema = SchemaLib.encode(SchemaType.Uint8, SchemaType.Uint16, SchemaType.Uint8, SchemaType.Uint16); bytes32 table = keccak256("some.table"); StoreCore.registerSchema(table, schema); @@ -176,7 +176,7 @@ contract StoreCoreTest is Test, StoreView { function testSetAndGetStaticDataSpanningWords() public { // Register table's schema - Schema schema = Schema_.encode(SchemaType.Uint128, SchemaType.Uint256); + Schema schema = SchemaLib.encode(SchemaType.Uint128, SchemaType.Uint256); bytes32 table = keccak256("some.table"); StoreCore.registerSchema(table, schema); @@ -204,7 +204,7 @@ contract StoreCoreTest is Test, StoreView { { // Register table's schema - Schema schema = Schema_.encode(SchemaType.Uint128, SchemaType.Uint32Array, SchemaType.Uint32Array); + Schema schema = SchemaLib.encode(SchemaType.Uint128, SchemaType.Uint32Array, SchemaType.Uint32Array); StoreCore.registerSchema(table, schema); } @@ -232,7 +232,7 @@ contract StoreCoreTest is Test, StoreView { uint16[] memory dynamicLengths = new uint16[](2); dynamicLengths[0] = uint16(secondDataBytes.length); dynamicLengths[1] = uint16(thirdDataBytes.length); - encodedDynamicLength = PackedCounter_.pack(dynamicLengths); + encodedDynamicLength = PackedCounterLib.pack(dynamicLengths); } // Concat data @@ -274,7 +274,7 @@ contract StoreCoreTest is Test, StoreView { { // Register table's schema - Schema schema = Schema_.encode( + Schema schema = SchemaLib.encode( SchemaType.Uint128, SchemaType.Uint256, SchemaType.Uint32Array, @@ -385,7 +385,7 @@ contract StoreCoreTest is Test, StoreView { assertEq(keccak256(loadedData), keccak256(fourthDataBytes)); // Verify all fields are correct - PackedCounter encodedLengths = PackedCounter_.pack(uint16(thirdDataBytes.length), uint16(fourthDataBytes.length)); + PackedCounter encodedLengths = PackedCounterLib.pack(uint16(thirdDataBytes.length), uint16(fourthDataBytes.length)); assertEq( keccak256(StoreCore.getRecord(table, key)), keccak256(bytes.concat(firstDataBytes, secondDataBytes, encodedLengths.unwrap(), thirdDataBytes, fourthDataBytes)) @@ -396,7 +396,7 @@ contract StoreCoreTest is Test, StoreView { bytes32 table = keccak256("some.table"); // Register table's schema - Schema schema = Schema_.encode(SchemaType.Uint128, SchemaType.Uint32Array, SchemaType.Uint32Array); + Schema schema = SchemaLib.encode(SchemaType.Uint128, SchemaType.Uint32Array, SchemaType.Uint32Array); StoreCore.registerSchema(table, schema); bytes16 firstDataBytes = bytes16(0x0102030405060708090a0b0c0d0e0f10); @@ -423,7 +423,7 @@ contract StoreCoreTest is Test, StoreView { uint16[] memory dynamicLengths = new uint16[](2); dynamicLengths[0] = uint16(secondDataBytes.length); dynamicLengths[1] = uint16(thirdDataBytes.length); - encodedDynamicLength = PackedCounter_.pack(dynamicLengths); + encodedDynamicLength = PackedCounterLib.pack(dynamicLengths); } // Concat data @@ -453,7 +453,7 @@ contract StoreCoreTest is Test, StoreView { function testAccessEmptyData() public { bytes32 table = keccak256("some.table"); - Schema schema = Schema_.encode(SchemaType.Uint32, SchemaType.Uint32Array); + Schema schema = SchemaLib.encode(SchemaType.Uint32, SchemaType.Uint32Array); StoreCore.registerSchema(table, schema); @@ -480,7 +480,7 @@ contract StoreCoreTest is Test, StoreView { key[0] = keccak256("some key"); // Register table's schema - Schema schema = Schema_.encode(SchemaType.Uint128); + Schema schema = SchemaLib.encode(SchemaType.Uint128); StoreCore.registerSchema(table, schema); // Create subscriber @@ -521,7 +521,7 @@ contract StoreCoreTest is Test, StoreView { key[0] = keccak256("some key"); // Register table's schema - Schema schema = Schema_.encode(SchemaType.Uint128, SchemaType.Uint32Array); + Schema schema = SchemaLib.encode(SchemaType.Uint128, SchemaType.Uint32Array); StoreCore.registerSchema(table, schema); // Create subscriber @@ -533,7 +533,7 @@ contract StoreCoreTest is Test, StoreView { uint32[] memory arrayData = new uint32[](1); arrayData[0] = 0x01020304; bytes memory arrayDataBytes = Bytes.from(arrayData); - PackedCounter encodedArrayDataLength = PackedCounter_.pack(uint16(arrayDataBytes.length)); + PackedCounter encodedArrayDataLength = PackedCounterLib.pack(uint16(arrayDataBytes.length)); bytes memory dynamicData = bytes.concat(encodedArrayDataLength.unwrap(), arrayDataBytes); bytes memory staticData = bytes.concat(bytes16(0x0102030405060708090a0b0c0d0e0f10)); bytes memory data = bytes.concat(staticData, dynamicData); From 55952b3f55e1433d220e1eef61394f2235ac3fb3 Mon Sep 17 00:00:00 2001 From: alvrs Date: Wed, 1 Feb 2023 20:26:47 +0100 Subject: [PATCH 55/82] build(store): different way to solve gas report action detached head issue --- .github/workflows/gasreport.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/gasreport.yml b/.github/workflows/gasreport.yml index 278c922960..fbcee5d5f6 100644 --- a/.github/workflows/gasreport.yml +++ b/.github/workflows/gasreport.yml @@ -17,6 +17,8 @@ jobs: - name: git-checkout uses: actions/checkout@v2 + with: + ref: ${GITHUB_HEAD_REF} - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 @@ -36,6 +38,5 @@ jobs: run: | git config --local user.email "github-actions@github.com" git config --local user.name "Gas report" - git checkout "${GITHUB_HEAD_REF}" git commit -a -m "chore(store): update gas report" git push From fc61b4c50618ad03cb26fddafb277bc369b47cb6 Mon Sep 17 00:00:00 2001 From: alvrs Date: Wed, 1 Feb 2023 20:27:38 +0100 Subject: [PATCH 56/82] refactor(store): more consistency for error naming --- packages/store/src/IStore.sol | 2 +- packages/store/src/Schema.sol | 26 ++++++++++++------------- packages/store/src/StoreCore.sol | 2 +- packages/store/src/StoreView.sol | 14 ++++++------- packages/store/src/test/StoreCore.t.sol | 4 ++-- 5 files changed, 24 insertions(+), 24 deletions(-) diff --git a/packages/store/src/IStore.sol b/packages/store/src/IStore.sol index da3b83c806..03873cc80d 100644 --- a/packages/store/src/IStore.sol +++ b/packages/store/src/IStore.sol @@ -29,7 +29,7 @@ interface IStore { ) external; // Register hooks to be called when a record or field is set or deleted - function registerHooks(bytes32 table, IStoreHook hooks) external; + function registerHook(bytes32 table, IStoreHook hooks) external; // Set full record (including full dynamic data) function deleteRecord(bytes32 table, bytes32[] memory key) external; diff --git a/packages/store/src/Schema.sol b/packages/store/src/Schema.sol index 3ba1b5f79a..bbbe855243 100644 --- a/packages/store/src/Schema.sol +++ b/packages/store/src/Schema.sol @@ -13,8 +13,8 @@ type Schema is bytes32; using SchemaLib for Schema global; library SchemaLib { - error SchemaLibInvalidLength(uint256 length); - error SchemaLibStaticTypeAfterDynamicType(); + error SchemaLib_InvalidLength(uint256 length); + error SchemaLib_StaticTypeAfterDynamicType(); /************************************************************************ * @@ -26,7 +26,7 @@ library SchemaLib { * Encode the given schema into a single bytes32 */ function encode(SchemaType[] memory _schema) internal pure returns (Schema) { - if (_schema.length > 28) revert SchemaLibInvalidLength(_schema.length); + if (_schema.length > 28) revert SchemaLib_InvalidLength(_schema.length); bytes32 schema; uint16 length; uint8 staticFields; @@ -40,7 +40,7 @@ library SchemaLib { // Increase the static field count if the field is static if (staticByteLength > 0) { // Revert if we have seen a dynamic field before, but now we see a static field - if (hasDynamicFields) revert SchemaLibStaticTypeAfterDynamicType(); + if (hasDynamicFields) revert SchemaLib_StaticTypeAfterDynamicType(); staticFields++; } else { // Flag that we have seen a dynamic field @@ -56,7 +56,7 @@ library SchemaLib { // Require max 14 dynamic fields uint8 dynamicFields = uint8(_schema.length) - staticFields; - if (dynamicFields > 14) revert SchemaLibInvalidLength(dynamicFields); + if (dynamicFields > 14) revert SchemaLib_InvalidLength(dynamicFields); // Store total static length, and number of static and dynamic fields schema = Bytes.setBytes1(schema, 0, bytes1(bytes2(length))); // upper length byte @@ -182,17 +182,17 @@ library SchemaLib { return Schema.unwrap(schema) == bytes32(0); } - function validate(Schema schema) internal view { + function validate(Schema schema) internal pure { // Schema must not be empty - if (schema.isEmpty()) revert SchemaLibInvalidLength(0); + if (schema.isEmpty()) revert SchemaLib_InvalidLength(0); // Schema must have no more than 14 dynamic fields uint256 _numDynamicFields = schema.numDynamicFields(); - if (_numDynamicFields > 14) revert SchemaLibInvalidLength(_numDynamicFields); + if (_numDynamicFields > 14) revert SchemaLib_InvalidLength(_numDynamicFields); uint256 _numStaticFields = schema.numStaticFields(); // Schema must not have more than 28 fields in total - if (_numStaticFields + _numDynamicFields > 28) revert SchemaLibInvalidLength(_numStaticFields + _numDynamicFields); + if (_numStaticFields + _numDynamicFields > 28) revert SchemaLib_InvalidLength(_numStaticFields + _numDynamicFields); // No static field can be after a dynamic field uint256 countStaticFields; @@ -200,11 +200,11 @@ library SchemaLib { for (uint256 i; i < _numStaticFields + _numDynamicFields; ) { if (getStaticByteLength(schema.atIndex(i)) > 0) { // Static field in dynamic part - if (i >= _numStaticFields) revert SchemaLibStaticTypeAfterDynamicType(); + if (i >= _numStaticFields) revert SchemaLib_StaticTypeAfterDynamicType(); countStaticFields++; } else { // Dynamic field in static part - if (i < _numStaticFields) revert SchemaLibStaticTypeAfterDynamicType(); + if (i < _numStaticFields) revert SchemaLib_StaticTypeAfterDynamicType(); countDynamicFields++; } unchecked { @@ -213,10 +213,10 @@ library SchemaLib { } // Number of static fields must match - if (countStaticFields != _numStaticFields) revert SchemaLibInvalidLength(countStaticFields); + if (countStaticFields != _numStaticFields) revert SchemaLib_InvalidLength(countStaticFields); // Number of dynamic fields must match - if (countDynamicFields != _numDynamicFields) revert SchemaLibInvalidLength(countDynamicFields); + if (countDynamicFields != _numDynamicFields) revert SchemaLib_InvalidLength(countDynamicFields); } /** diff --git a/packages/store/src/StoreCore.sol b/packages/store/src/StoreCore.sol index 784ef46dc3..bfc5c82d32 100644 --- a/packages/store/src/StoreCore.sol +++ b/packages/store/src/StoreCore.sol @@ -100,7 +100,7 @@ library StoreCore { /* * Register hooks to be called when a record or field is set or deleted */ - function registerHooks(bytes32 table, IStoreHook hooks) external { + function registerHook(bytes32 table, IStoreHook hooks) external { HooksTable.push(table, address(hooks)); } diff --git a/packages/store/src/StoreView.sol b/packages/store/src/StoreView.sol index 9940a438e4..4dad55d2ca 100644 --- a/packages/store/src/StoreView.sol +++ b/packages/store/src/StoreView.sol @@ -8,7 +8,7 @@ import { Schema } from "./Schema.sol"; // Not abstract, so that it can be used as a base contract for testing and wherever write access is not needed contract StoreView is IStore { - error Store_BaseContractNotImplemented(); + error StoreView_NotImplemented(); constructor() { StoreCore.initialize(); @@ -19,7 +19,7 @@ contract StoreView is IStore { } function registerSchema(bytes32, Schema) public virtual { - revert Store_BaseContractNotImplemented(); + revert StoreView_NotImplemented(); } function setRecord( @@ -27,7 +27,7 @@ contract StoreView is IStore { bytes32[] memory, bytes memory ) public virtual { - revert Store_BaseContractNotImplemented(); + revert StoreView_NotImplemented(); } // Set partial data at schema index @@ -37,15 +37,15 @@ contract StoreView is IStore { uint8, bytes memory ) public virtual { - revert Store_BaseContractNotImplemented(); + revert StoreView_NotImplemented(); } - function registerHooks(bytes32, IStoreHook) public virtual { - revert Store_BaseContractNotImplemented(); + function registerHook(bytes32, IStoreHook) public virtual { + revert StoreView_NotImplemented(); } function deleteRecord(bytes32, bytes32[] memory) public virtual { - revert Store_BaseContractNotImplemented(); + revert StoreView_NotImplemented(); } // Get full record (including full array, load schema from storage) diff --git a/packages/store/src/test/StoreCore.t.sol b/packages/store/src/test/StoreCore.t.sol index 4cef44b131..8bad9e2626 100644 --- a/packages/store/src/test/StoreCore.t.sol +++ b/packages/store/src/test/StoreCore.t.sol @@ -487,7 +487,7 @@ contract StoreCoreTest is Test, StoreView { MirrorSubscriber subscriber = new MirrorSubscriber(table, schema); // !gasreport register subscriber - StoreCore.registerHooks(table, subscriber); + StoreCore.registerHook(table, subscriber); bytes memory data = bytes.concat(bytes16(0x0102030405060708090a0b0c0d0e0f10)); @@ -528,7 +528,7 @@ contract StoreCoreTest is Test, StoreView { MirrorSubscriber subscriber = new MirrorSubscriber(table, schema); // !gasreport register subscriber - StoreCore.registerHooks(table, subscriber); + StoreCore.registerHook(table, subscriber); uint32[] memory arrayData = new uint32[](1); arrayData[0] = 0x01020304; From f4504f5961f8f21c5db27188776ee20982a84fad Mon Sep 17 00:00:00 2001 From: alvrs Date: Wed, 1 Feb 2023 20:37:08 +0100 Subject: [PATCH 57/82] build: fix gas report action --- .github/workflows/gasreport.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gasreport.yml b/.github/workflows/gasreport.yml index fbcee5d5f6..6b283c45d3 100644 --- a/.github/workflows/gasreport.yml +++ b/.github/workflows/gasreport.yml @@ -18,7 +18,7 @@ jobs: - name: git-checkout uses: actions/checkout@v2 with: - ref: ${GITHUB_HEAD_REF} + ref: ${{ env.GITHUB_HEAD_REF }} - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 From 3218469661c4d2bf780bd7e266470b2c57caf23f Mon Sep 17 00:00:00 2001 From: alvrs Date: Wed, 1 Feb 2023 20:43:31 +0100 Subject: [PATCH 58/82] refactor: rename gas report action --- .github/workflows/gasreport.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gasreport.yml b/.github/workflows/gasreport.yml index 6b283c45d3..37af5c2631 100644 --- a/.github/workflows/gasreport.yml +++ b/.github/workflows/gasreport.yml @@ -9,7 +9,7 @@ on: jobs: gas-report: runs-on: ubuntu-22.04 - name: Run tests + name: Generate gas report steps: - uses: actions/setup-node@v3 with: From df8889cfd6e75e63716f267bf5a798fc805e94db Mon Sep 17 00:00:00 2001 From: alvrs Date: Wed, 1 Feb 2023 21:04:19 +0100 Subject: [PATCH 59/82] refactor: hopefully actually fix gas report action --- .github/workflows/gasreport.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/gasreport.yml b/.github/workflows/gasreport.yml index 37af5c2631..d7b291b415 100644 --- a/.github/workflows/gasreport.yml +++ b/.github/workflows/gasreport.yml @@ -16,9 +16,9 @@ jobs: node-version: 16 - name: git-checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: - ref: ${{ env.GITHUB_HEAD_REF }} + ref: ${{ github.event.pull_request.head.sha }} - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 From fba6ffb43246c2fcd27c73224df4d086149a26b2 Mon Sep 17 00:00:00 2001 From: alvrs Date: Wed, 1 Feb 2023 21:09:00 +0100 Subject: [PATCH 60/82] refactor(store): move StoreCore internal functions to StoreCoreInternal --- packages/store/src/StoreCore.sol | 207 +++++++++++++----------- packages/store/src/test/StoreCore.t.sol | 25 +-- 2 files changed, 130 insertions(+), 102 deletions(-) diff --git a/packages/store/src/StoreCore.sol b/packages/store/src/StoreCore.sol index bfc5c82d32..32995399d2 100644 --- a/packages/store/src/StoreCore.sol +++ b/packages/store/src/StoreCore.sol @@ -16,7 +16,6 @@ import { IStoreHook } from "./IStore.sol"; // - Turn all storage pointer to uint256 for consistency (uint256 is better than bytes32 because it's easier to do arithmetic on) // - Change Storage library functions to make it clearer which argument is offset and which is length // - Streamline naming in Storage and Memory libraries (probably just use load and store instead of read and write?) -// - Move all internal functions to a separate StoreCoreInternal library to discourage using it 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 @@ -24,9 +23,6 @@ library StoreCore { event MudStoreSetField(bytes32 table, bytes32[] key, uint8 schemaIndex, bytes data); event MudStoreDeleteRecord(bytes32 table, bytes32[] key); - bytes32 internal constant SLOT = keccak256("mud.store"); - bytes32 internal constant SCHEMA_TABLE = keccak256("mud.store.table.schema"); - error StoreCore_TableAlreadyExists(bytes32 table); error StoreCore_TableNotFound(bytes32 table); error StoreCore_NotImplemented(); @@ -68,27 +64,14 @@ library StoreCore { } // Register the schema - _registerSchemaUnchecked(table, schema); - } - - /** - * Register a new table schema without validity checks - */ - function _registerSchemaUnchecked(bytes32 table, Schema schema) internal { - bytes32[] memory key = new bytes32[](1); - key[0] = table; - bytes32 location = _getStaticDataLocation(SCHEMA_TABLE, key); - Storage.write(location, schema.unwrap()); + StoreCoreInternal._registerSchemaUnchecked(table, schema); } /** * Get the schema for the given table */ function getSchema(bytes32 table) internal view returns (Schema schema) { - bytes32[] memory key = new bytes32[](1); - key[0] = table; - bytes32 location = _getStaticDataLocation(SCHEMA_TABLE, key); - return Schema.wrap(Storage.read(location)); + return StoreCoreInternal._getSchema(table); } /************************************************************************ @@ -144,7 +127,7 @@ library StoreCore { } // Store the static data at the static data location - bytes32 staticDataLocation = _getStaticDataLocation(table, key); + bytes32 staticDataLocation = StoreCoreInternal._getStaticDataLocation(table, key); uint256 memoryPointer = Memory.dataPointer(data); Storage.write(staticDataLocation, 0, memoryPointer, staticLength); memoryPointer += staticLength + 32; // move the memory pointer to the start of the dynamic data (skip the encoded dynamic length) @@ -153,14 +136,14 @@ library StoreCore { if (schema.numDynamicFields() == 0) return; // Store the dynamic data length at the dynamic data length location - bytes32 dynamicDataLengthLocation = _getDynamicDataLengthLocation(table, key); + bytes32 dynamicDataLengthLocation = StoreCoreInternal._getDynamicDataLengthLocation(table, key); Storage.write(dynamicDataLengthLocation, dynamicLength.unwrap()); // For every dynamic element, slice off the dynamic data and store it at the dynamic location bytes32 dynamicDataLocation; uint256 dynamicDataLength; for (uint8 i; i < schema.numDynamicFields(); ) { - dynamicDataLocation = _getDynamicDataLocation(table, key, i); + dynamicDataLocation = StoreCoreInternal._getDynamicDataLocation(table, key, i); dynamicDataLength = dynamicLength.atIndex(i); Storage.write(dynamicDataLocation, 0, memoryPointer, dynamicDataLength); memoryPointer += dynamicDataLength; // move the memory pointer to the start of the next dynamic data @@ -189,50 +172,15 @@ library StoreCore { } if (schemaIndex < schema.numStaticFields()) { - _setStaticField(table, key, schema, schemaIndex, data); + StoreCoreInternal._setStaticField(table, key, schema, schemaIndex, data); } else { - _setDynamicField(table, key, schema, schemaIndex, data); + StoreCoreInternal._setDynamicField(table, key, schema, schemaIndex, data); } // Emit event to notify indexers emit MudStoreSetField(table, key, schemaIndex, data); } - function _setStaticField( - bytes32 table, - bytes32[] memory key, - Schema schema, - uint8 schemaIndex, - bytes memory data - ) internal { - // verify the value has the correct length for the field - SchemaType schemaType = schema.atIndex(schemaIndex); - if (getStaticByteLength(schemaType) != data.length) - revert StoreCore_InvalidDataLength(getStaticByteLength(schemaType), data.length); - - // Store the provided value in storage - bytes32 location = _getStaticDataLocation(table, key); - uint256 offset = _getStaticDataOffset(schema, schemaIndex); - Storage.write(location, offset, data); - } - - function _setDynamicField( - bytes32 table, - bytes32[] memory key, - Schema schema, - uint8 schemaIndex, - bytes memory data - ) internal { - uint8 dynamicSchemaIndex = schemaIndex - schema.numStaticFields(); - - // Update the dynamic data length - _setDynamicDataLengthAtIndex(table, key, dynamicSchemaIndex, data.length); - - // Store the provided value in storage - bytes32 dynamicDataLocation = _getDynamicDataLocation(table, key, dynamicSchemaIndex); - Storage.write(dynamicDataLocation, data); - } - function deleteRecord(bytes32 table, bytes32[] memory key) internal { // Get schema for this table Schema schema = getSchema(table); @@ -245,7 +193,7 @@ library StoreCore { } // Delete static data - bytes32 staticDataLocation = _getStaticDataLocation(table, key); + bytes32 staticDataLocation = StoreCoreInternal._getStaticDataLocation(table, key); Storage.write(staticDataLocation, 0, new bytes(schema.staticDataLength())); // If there are no dynamic fields, we're done @@ -253,9 +201,9 @@ library StoreCore { // Delete dynamic data bytes32 dynamicDataLocation; - PackedCounter encodedLengths = _loadEncodedDynamicDataLength(table, key); + PackedCounter encodedLengths = StoreCoreInternal._loadEncodedDynamicDataLength(table, key); for (uint8 i; i < schema.numDynamicFields(); ) { - dynamicDataLocation = _getDynamicDataLocation(table, key, i); + dynamicDataLocation = StoreCoreInternal._getDynamicDataLocation(table, key, i); Storage.write(dynamicDataLocation, 0, new bytes(encodedLengths.atIndex(i))); unchecked { i++; @@ -263,7 +211,7 @@ library StoreCore { } // Delete dynamic data length - bytes32 dynamicDataLengthLocation = _getDynamicDataLengthLocation(table, key); + bytes32 dynamicDataLengthLocation = StoreCoreInternal._getDynamicDataLengthLocation(table, key); Storage.write(dynamicDataLengthLocation, bytes32(0)); // Emit event to notify indexers @@ -302,7 +250,7 @@ library StoreCore { PackedCounter dynamicDataLength; uint256 numDynamicFields = schema.numDynamicFields(); if (numDynamicFields > 0) { - dynamicDataLength = _loadEncodedDynamicDataLength(table, key); + dynamicDataLength = StoreCoreInternal._loadEncodedDynamicDataLength(table, key); outputLength += 32 + dynamicDataLength.total(); // encoded length + data } @@ -310,7 +258,7 @@ library StoreCore { Buffer buffer = Buffer_.allocate(uint128(outputLength)); // Load the static data from storage and append it to the buffer - buffer.append(getStaticData(table, key, schema)); + buffer.append(StoreCoreInternal._getStaticData(table, key, schema)); // Early return if there are no dynamic fields if (dynamicDataLength.total() == 0) return buffer.toBytes(); @@ -320,7 +268,7 @@ library StoreCore { // Append dynamic data to the buffer for (uint8 i; i < numDynamicFields; i++) { - uint256 dynamicDataLocation = uint256(_getDynamicDataLocation(table, key, i)); + uint256 dynamicDataLocation = uint256(StoreCoreInternal._getDynamicDataLocation(table, key, i)); Storage.read(dynamicDataLocation, 0, dynamicDataLength.atIndex(i), buffer); } @@ -329,54 +277,131 @@ library StoreCore { } /** - * Get full static record for the given table and key tuple (loading schema from storage) + * Get a single field from the given table and key tuple (loading schema from storage) */ - function getStaticData(bytes32 table, bytes32[] memory key) internal view returns (bytes memory) { + function getField( + bytes32 table, + bytes32[] memory key, + uint8 schemaIndex + ) internal view returns (bytes memory) { Schema schema = getSchema(table); - return getStaticData(table, key, schema); + return getField(table, key, schemaIndex, schema); } /** - * Get full static data for the given table and key tuple, with the given schema + * Get a single field from the given table and key tuple, with the given schema */ - function getStaticData( + function getField( bytes32 table, bytes32[] memory key, + uint8 schemaIndex, Schema schema ) internal view returns (bytes memory) { - if (schema.staticDataLength() == 0) return new bytes(0); + if (schemaIndex < schema.numStaticFields()) { + return StoreCoreInternal._getStaticField(table, key, schemaIndex, schema); + } else { + return StoreCoreInternal._getDynamicField(table, key, schemaIndex, schema); + } + } +} - // Load the data from storage - bytes32 location = _getStaticDataLocation(table, key); - return Storage.read(location, schema.staticDataLength()); +library StoreCoreInternal { + bytes32 internal constant SLOT = keccak256("mud.store"); + bytes32 internal constant SCHEMA_TABLE = keccak256("mud.store.table.schema"); + + error StoreCore_InvalidDataLength(uint256 expected, uint256 received); + + /************************************************************************ + * + * SCHEMA + * + ************************************************************************/ + + function _getSchema(bytes32 table) internal view returns (Schema) { + bytes32[] memory key = new bytes32[](1); + key[0] = table; + bytes32 location = StoreCoreInternal._getStaticDataLocation(SCHEMA_TABLE, key); + return Schema.wrap(Storage.read(location)); } /** - * Get a single field from the given table and key tuple (loading schema from storage) + * Register a new table schema without validity checks */ - function getField( + function _registerSchemaUnchecked(bytes32 table, Schema schema) internal { + bytes32[] memory key = new bytes32[](1); + key[0] = table; + bytes32 location = _getStaticDataLocation(SCHEMA_TABLE, key); + Storage.write(location, schema.unwrap()); + } + + /************************************************************************ + * + * SET DATA + * + ************************************************************************/ + + function _setStaticField( bytes32 table, bytes32[] memory key, - uint8 schemaIndex - ) internal view returns (bytes memory) { - Schema schema = getSchema(table); - return getField(table, key, schemaIndex, schema); + Schema schema, + uint8 schemaIndex, + bytes memory data + ) internal { + // verify the value has the correct length for the field + SchemaType schemaType = schema.atIndex(schemaIndex); + if (getStaticByteLength(schemaType) != data.length) + revert StoreCore_InvalidDataLength(getStaticByteLength(schemaType), data.length); + + // Store the provided value in storage + bytes32 location = _getStaticDataLocation(table, key); + uint256 offset = _getStaticDataOffset(schema, schemaIndex); + Storage.write(location, offset, data); + } + + function _setDynamicField( + bytes32 table, + bytes32[] memory key, + Schema schema, + uint8 schemaIndex, + bytes memory data + ) internal { + uint8 dynamicSchemaIndex = schemaIndex - schema.numStaticFields(); + + // Update the dynamic data length + _setDynamicDataLengthAtIndex(table, key, dynamicSchemaIndex, data.length); + + // Store the provided value in storage + bytes32 dynamicDataLocation = _getDynamicDataLocation(table, key, dynamicSchemaIndex); + Storage.write(dynamicDataLocation, data); } /** - * Get a single field from the given table and key tuple, with the given schema + * Get full static record for the given table and key tuple (loading schema from storage) */ - function getField( + function _getStaticData(bytes32 table, bytes32[] memory key) internal view returns (bytes memory) { + Schema schema = _getSchema(table); + return _getStaticData(table, key, schema); + } + + /************************************************************************ + * + * GET DATA + * + ************************************************************************/ + + /** + * Get full static data for the given table and key tuple, with the given schema + */ + function _getStaticData( bytes32 table, bytes32[] memory key, - uint8 schemaIndex, Schema schema ) internal view returns (bytes memory) { - if (schemaIndex < schema.numStaticFields()) { - return _getStaticField(table, key, schemaIndex, schema); - } else { - return _getDynamicField(table, key, schemaIndex, schema); - } + if (schema.staticDataLength() == 0) return new bytes(0); + + // Load the data from storage + bytes32 location = _getStaticDataLocation(table, key); + return Storage.read(location, schema.staticDataLength()); } /** @@ -418,7 +443,7 @@ library StoreCore { /************************************************************************ * - * INTERNAL HELPER FUNCTIONS + * HELPER FUNCTIONS * ************************************************************************/ @@ -497,7 +522,7 @@ library StoreCore { } // Overloads for single key and some fixed length array keys for better devex -library StoreCoreExt { +library StoreCoreExtended { /************************************************************************ * * SET DATA diff --git a/packages/store/src/test/StoreCore.t.sol b/packages/store/src/test/StoreCore.t.sol index 8bad9e2626..067d5c3b7d 100644 --- a/packages/store/src/test/StoreCore.t.sol +++ b/packages/store/src/test/StoreCore.t.sol @@ -2,7 +2,7 @@ pragma solidity >=0.8.0; import "forge-std/Test.sol"; -import { StoreCore } from "../StoreCore.sol"; +import { StoreCore, StoreCoreInternal } from "../StoreCore.sol"; import { Utils } from "../Utils.sol"; import { Bytes } from "../Bytes.sol"; import { SchemaType } from "../Types.sol"; @@ -109,27 +109,27 @@ contract StoreCoreTest is Test, StoreView { // Set dynamic data length of dynamic index 0 // !gasreport set dynamic length of dynamic index 0 - StoreCore._setDynamicDataLengthAtIndex(table, key, 0, 10); + StoreCoreInternal._setDynamicDataLengthAtIndex(table, key, 0, 10); - PackedCounter encodedLength = StoreCore._loadEncodedDynamicDataLength(table, key); + PackedCounter encodedLength = StoreCoreInternal._loadEncodedDynamicDataLength(table, key); assertEq(encodedLength.atIndex(0), 10); assertEq(encodedLength.atIndex(1), 0); assertEq(encodedLength.total(), 10); // Set dynamic data length of dynamic index 1 // !gasreport set dynamic length of dynamic index 1 - StoreCore._setDynamicDataLengthAtIndex(table, key, 1, 99); + StoreCoreInternal._setDynamicDataLengthAtIndex(table, key, 1, 99); - encodedLength = StoreCore._loadEncodedDynamicDataLength(table, key); + encodedLength = StoreCoreInternal._loadEncodedDynamicDataLength(table, key); assertEq(encodedLength.atIndex(0), 10); assertEq(encodedLength.atIndex(1), 99); assertEq(encodedLength.total(), 109); // Reduce dynamic data length of dynamic index 0 again // !gasreport reduce dynamic length of dynamic index 0 - StoreCore._setDynamicDataLengthAtIndex(table, key, 0, 5); + StoreCoreInternal._setDynamicDataLengthAtIndex(table, key, 0, 5); - encodedLength = StoreCore._loadEncodedDynamicDataLength(table, key); + encodedLength = StoreCoreInternal._loadEncodedDynamicDataLength(table, key); assertEq(encodedLength.atIndex(0), 5); assertEq(encodedLength.atIndex(1), 99); assertEq(encodedLength.total(), 104); @@ -326,10 +326,13 @@ contract StoreCoreTest is Test, StoreView { assertEq(bytes16(StoreCore.getField(table, key, 0)), bytes16(firstDataBytes)); // Verify the full static data is correct - assertEq(StoreCore.getStaticData(table, key).length, 48); - assertEq(Bytes.slice16(StoreCore.getStaticData(table, key), 0), firstDataBytes); - assertEq(Bytes.slice32(StoreCore.getStaticData(table, key), 16), secondDataBytes); - assertEq(keccak256(StoreCore.getStaticData(table, key)), keccak256(bytes.concat(firstDataBytes, secondDataBytes))); + assertEq(StoreCoreInternal._getStaticData(table, key).length, 48); + assertEq(Bytes.slice16(StoreCoreInternal._getStaticData(table, key), 0), firstDataBytes); + assertEq(Bytes.slice32(StoreCoreInternal._getStaticData(table, key), 16), secondDataBytes); + assertEq( + keccak256(StoreCoreInternal._getStaticData(table, key)), + keccak256(bytes.concat(firstDataBytes, secondDataBytes)) + ); //////////////// // Dynamic data From 09725bf08b1ad74e55b3e8afdbfe47acbc77f9e5 Mon Sep 17 00:00:00 2001 From: alvrs Date: Wed, 1 Feb 2023 21:20:31 +0100 Subject: [PATCH 61/82] refactor(store): rename write to store and read to load in Storage and Memory --- packages/store/src/Memory.sol | 12 +-- packages/store/src/Storage.sol | 102 +++++++++++++------------- packages/store/src/StoreCore.sol | 34 ++++----- packages/store/src/test/Storage.t.sol | 32 ++++---- 4 files changed, 90 insertions(+), 90 deletions(-) diff --git a/packages/store/src/Memory.sol b/packages/store/src/Memory.sol index 1ea2d4f8fb..fe8d9fb21b 100644 --- a/packages/store/src/Memory.sol +++ b/packages/store/src/Memory.sol @@ -5,21 +5,21 @@ import { Utils } from "./Utils.sol"; import { Bytes } from "./Bytes.sol"; library Memory { - function read(uint256 memoryPointer) internal pure returns (bytes32 data) { - return read(bytes32(memoryPointer)); + function load(uint256 memoryPointer) internal pure returns (bytes32 data) { + return load(bytes32(memoryPointer)); } - function read(bytes32 memoryPointer) internal pure returns (bytes32 data) { + function load(bytes32 memoryPointer) internal pure returns (bytes32 data) { assembly { data := mload(memoryPointer) } } - function read(uint256 memoryPointer, uint256 offset) internal pure returns (bytes32 data) { - return read(bytes32(memoryPointer), offset); + function load(uint256 memoryPointer, uint256 offset) internal pure returns (bytes32 data) { + return load(bytes32(memoryPointer), offset); } - function read(bytes32 memoryPointer, uint256 offset) internal pure returns (bytes32 data) { + function load(bytes32 memoryPointer, uint256 offset) internal pure returns (bytes32 data) { assembly { data := mload(add(memoryPointer, offset)) } diff --git a/packages/store/src/Storage.sol b/packages/store/src/Storage.sol index 33c25c71cf..f1865eecdf 100644 --- a/packages/store/src/Storage.sol +++ b/packages/store/src/Storage.sol @@ -8,31 +8,31 @@ import { Memory } from "./Memory.sol"; import "./Buffer.sol"; library Storage { - function write(bytes32 storagePointer, bytes memory data) internal { - write(uint256(storagePointer), 0, data); + function store(bytes32 storagePointer, bytes memory data) internal { + store(uint256(storagePointer), 0, data); } - function write(uint256 storagePointer, bytes memory data) internal { - write(storagePointer, 0, data); + function store(uint256 storagePointer, bytes memory data) internal { + store(storagePointer, 0, data); } - function write(bytes32 storagePointer, bytes32 data) internal { - _writeWord(uint256(storagePointer), data); + function store(bytes32 storagePointer, bytes32 data) internal { + _storeWord(uint256(storagePointer), data); } - function write(uint256 storagePointer, bytes32 data) internal { - _writeWord(storagePointer, data); + function store(uint256 storagePointer, bytes32 data) internal { + _storeWord(storagePointer, data); } - function write( + function store( bytes32 storagePointer, uint256 offset, bytes memory data ) internal { - write(uint256(storagePointer), offset, data); + store(uint256(storagePointer), offset, data); } - function write( + function store( uint256 storagePointer, uint256 offset, bytes memory data @@ -41,23 +41,23 @@ library Storage { assembly { memoryPointer := add(data, 0x20) } - write(storagePointer, offset, memoryPointer, data.length); + store(storagePointer, offset, memoryPointer, data.length); } - function write( + function store( bytes32 storagePointer, uint256 offset, uint256 memoryPointer, uint256 length ) internal { - write(uint256(storagePointer), offset, memoryPointer, length); + store(uint256(storagePointer), offset, memoryPointer, length); } /** - * @dev Write raw bytes to storage at the given storagePointer and offset (keeping the rest of the word intact) + * @dev store raw bytes to storage at the given storagePointer and offset (keeping the rest of the word intact) * TODO: this implementation is optimized for readability, but not very gas efficient. We should optimize this using assembly once we've settled on a spec. */ - function write( + function store( uint256 storagePointer, uint256 offset, uint256 memoryPointer, @@ -73,72 +73,72 @@ library Storage { for (uint256 i; i < numWords; i++) { // If this is the first word, and there is an offset, apply a mask to beginning if ((i == 0 && offset > 0)) { - uint256 _lengthToWrite = length + offset > 32 ? 32 - offset : length; // // the number of bytes to write - _writePartialWord( + uint256 _lengthTostore = length + offset > 32 ? 32 - offset : length; // // the number of bytes to store + _storePartialWord( storagePointer, // the word to update offset, // the offset in bytes to start writing - _lengthToWrite, - Memory.read(memoryPointer) // Pass the first 32 bytes of the data + _lengthTostore, + Memory.load(memoryPointer) // Pass the first 32 bytes of the data ); - bytesWritten += _lengthToWrite; + bytesWritten += _lengthTostore; // If this is the last word, and there is a partial word, apply a mask to the end } else if (i == numWords - 1 && (length + offset) % 32 > 0) { - _writePartialWord( + _storePartialWord( storagePointer + i, // the word to update 0, // the offset in bytes to start writing - (length + offset) % 32, // the number of bytes to write - Memory.read(memoryPointer, bytesWritten) // the data to write + (length + offset) % 32, // the number of bytes to store + Memory.load(memoryPointer, bytesWritten) // the data to store ); - // Else, just write the word + // Else, just store the word } else { - _writeWord(storagePointer + i, Memory.read(memoryPointer, bytesWritten)); + _storeWord(storagePointer + i, Memory.load(memoryPointer, bytesWritten)); bytesWritten += 32; } } } - function read(bytes32 storagePointer) internal view returns (bytes32) { + function load(bytes32 storagePointer) internal view returns (bytes32) { return _loadWord(uint256(storagePointer)); } - function read(uint256 storagePointer) internal view returns (bytes32) { + function load(uint256 storagePointer) internal view returns (bytes32) { return _loadWord(storagePointer); } - function read(bytes32 storagePointer, uint256 length) internal view returns (bytes memory) { - return read(uint256(storagePointer), 0, length); + function load(bytes32 storagePointer, uint256 length) internal view returns (bytes memory) { + return load(uint256(storagePointer), 0, length); } - function read(uint256 storagePointer, uint256 length) internal view returns (bytes memory) { - return read(storagePointer, 0, length); + function load(uint256 storagePointer, uint256 length) internal view returns (bytes memory) { + return load(storagePointer, 0, length); } - function read( + function load( bytes32 storagePointer, uint256 offset, uint256 length ) internal view returns (bytes memory) { - return read(uint256(storagePointer), offset, length); + return load(uint256(storagePointer), offset, length); } /** - * @dev Read raw bytes from storage at the given storagePointer, offset, and length + * @dev load raw bytes from storage at the given storagePointer, offset, and length */ - function read( + function load( uint256 storagePointer, uint256 offset, uint256 length ) internal view returns (bytes memory) { Buffer buffer = Buffer_.allocate(uint128(length)); - read(storagePointer, offset, length, buffer); + load(storagePointer, offset, length, buffer); return buffer.toBytes(); } /** * @dev Append raw bytes from storage at the given storagePointer, offset, and length to the given buffer */ - function read( + function load( uint256 storagePointer, uint256 offset, uint256 length, @@ -149,34 +149,34 @@ library Storage { offset %= 32; uint256 numWords = Utils.divCeil(length + offset, 32); - uint256 _lengthToRead; + uint256 _lengthToload; for (uint256 i; i < numWords; i++) { // If this is the first word, and there is an offset, apply a mask to beginning (and possibly the end if length + offset is less than 32) if ((i == 0 && offset > 0)) { - _lengthToRead = length + offset > 32 ? 32 - offset : length; // the number of bytes to read + _lengthToload = length + offset > 32 ? 32 - offset : length; // the number of bytes to load buffer.appendUnchecked( _loadPartialWord( storagePointer, // the slot to start loading from - offset, // the offset in bytes to start reading from - _lengthToRead + offset, // the offset in bytes to start loading from + _lengthToload ), - uint128(_lengthToRead) + uint128(_lengthToload) ); // If this is the last word, and there is a partial word, apply a mask to the end } else if (i == numWords - 1 && (length + offset) % 32 > 0) { - _lengthToRead = (length + offset) % 32; // the relevant length of the trailing word + _lengthToload = (length + offset) % 32; // the relevant length of the trailing word buffer.appendUnchecked( _loadPartialWord( - storagePointer + i, // the word to read from - 0, // the offset in bytes to start reading from - _lengthToRead + storagePointer + i, // the word to load from + 0, // the offset in bytes to start loading from + _lengthToload ), - uint128(_lengthToRead) + uint128(_lengthToload) ); - // Else, just read the word + // Else, just load the word } else { buffer.appendUnchecked(_loadWord(storagePointer + i), 32); } @@ -210,13 +210,13 @@ library Storage { return (storageValue << (offset * 8)) & bytes32(Utils.leftMask(length * 8)); } - function _writeWord(uint256 storagePointer, bytes32 data) internal { + function _storeWord(uint256 storagePointer, bytes32 data) internal { assembly { sstore(storagePointer, data) } } - function _writePartialWord( + function _storePartialWord( uint256 storagePointer, uint256 offset, // in bytes uint256 length, // in bytes diff --git a/packages/store/src/StoreCore.sol b/packages/store/src/StoreCore.sol index 32995399d2..50a3a28b4a 100644 --- a/packages/store/src/StoreCore.sol +++ b/packages/store/src/StoreCore.sol @@ -129,7 +129,7 @@ library StoreCore { // Store the static data at the static data location bytes32 staticDataLocation = StoreCoreInternal._getStaticDataLocation(table, key); uint256 memoryPointer = Memory.dataPointer(data); - Storage.write(staticDataLocation, 0, memoryPointer, staticLength); + Storage.store(staticDataLocation, 0, memoryPointer, staticLength); memoryPointer += staticLength + 32; // move the memory pointer to the start of the dynamic data (skip the encoded dynamic length) // If there is no dynamic data, we're done @@ -137,7 +137,7 @@ library StoreCore { // Store the dynamic data length at the dynamic data length location bytes32 dynamicDataLengthLocation = StoreCoreInternal._getDynamicDataLengthLocation(table, key); - Storage.write(dynamicDataLengthLocation, dynamicLength.unwrap()); + Storage.store(dynamicDataLengthLocation, dynamicLength.unwrap()); // For every dynamic element, slice off the dynamic data and store it at the dynamic location bytes32 dynamicDataLocation; @@ -145,7 +145,7 @@ library StoreCore { for (uint8 i; i < schema.numDynamicFields(); ) { dynamicDataLocation = StoreCoreInternal._getDynamicDataLocation(table, key, i); dynamicDataLength = dynamicLength.atIndex(i); - Storage.write(dynamicDataLocation, 0, memoryPointer, dynamicDataLength); + Storage.store(dynamicDataLocation, 0, memoryPointer, dynamicDataLength); memoryPointer += dynamicDataLength; // move the memory pointer to the start of the next dynamic data unchecked { i++; @@ -194,7 +194,7 @@ library StoreCore { // Delete static data bytes32 staticDataLocation = StoreCoreInternal._getStaticDataLocation(table, key); - Storage.write(staticDataLocation, 0, new bytes(schema.staticDataLength())); + Storage.store(staticDataLocation, 0, new bytes(schema.staticDataLength())); // If there are no dynamic fields, we're done if (schema.numDynamicFields() == 0) return; @@ -204,7 +204,7 @@ library StoreCore { PackedCounter encodedLengths = StoreCoreInternal._loadEncodedDynamicDataLength(table, key); for (uint8 i; i < schema.numDynamicFields(); ) { dynamicDataLocation = StoreCoreInternal._getDynamicDataLocation(table, key, i); - Storage.write(dynamicDataLocation, 0, new bytes(encodedLengths.atIndex(i))); + Storage.store(dynamicDataLocation, 0, new bytes(encodedLengths.atIndex(i))); unchecked { i++; } @@ -212,7 +212,7 @@ library StoreCore { // Delete dynamic data length bytes32 dynamicDataLengthLocation = StoreCoreInternal._getDynamicDataLengthLocation(table, key); - Storage.write(dynamicDataLengthLocation, bytes32(0)); + Storage.store(dynamicDataLengthLocation, bytes32(0)); // Emit event to notify indexers emit MudStoreDeleteRecord(table, key); @@ -269,7 +269,7 @@ library StoreCore { // Append dynamic data to the buffer for (uint8 i; i < numDynamicFields; i++) { uint256 dynamicDataLocation = uint256(StoreCoreInternal._getDynamicDataLocation(table, key, i)); - Storage.read(dynamicDataLocation, 0, dynamicDataLength.atIndex(i), buffer); + Storage.load(dynamicDataLocation, 0, dynamicDataLength.atIndex(i), buffer); } // Return the buffer as bytes @@ -321,7 +321,7 @@ library StoreCoreInternal { bytes32[] memory key = new bytes32[](1); key[0] = table; bytes32 location = StoreCoreInternal._getStaticDataLocation(SCHEMA_TABLE, key); - return Schema.wrap(Storage.read(location)); + return Schema.wrap(Storage.load(location)); } /** @@ -331,7 +331,7 @@ library StoreCoreInternal { bytes32[] memory key = new bytes32[](1); key[0] = table; bytes32 location = _getStaticDataLocation(SCHEMA_TABLE, key); - Storage.write(location, schema.unwrap()); + Storage.store(location, schema.unwrap()); } /************************************************************************ @@ -355,7 +355,7 @@ library StoreCoreInternal { // Store the provided value in storage bytes32 location = _getStaticDataLocation(table, key); uint256 offset = _getStaticDataOffset(schema, schemaIndex); - Storage.write(location, offset, data); + Storage.store(location, offset, data); } function _setDynamicField( @@ -372,7 +372,7 @@ library StoreCoreInternal { // Store the provided value in storage bytes32 dynamicDataLocation = _getDynamicDataLocation(table, key, dynamicSchemaIndex); - Storage.write(dynamicDataLocation, data); + Storage.store(dynamicDataLocation, data); } /** @@ -401,7 +401,7 @@ library StoreCoreInternal { // Load the data from storage bytes32 location = _getStaticDataLocation(table, key); - return Storage.read(location, schema.staticDataLength()); + return Storage.load(location, schema.staticDataLength()); } /** @@ -421,7 +421,7 @@ library StoreCoreInternal { // Load the data from storage - return Storage.read(location, offset, dataLength); + return Storage.load(location, offset, dataLength); } /** @@ -438,7 +438,7 @@ library StoreCoreInternal { bytes32 location = _getDynamicDataLocation(table, key, dynamicSchemaIndex); uint256 dataLength = _loadEncodedDynamicDataLength(table, key).atIndex(dynamicSchemaIndex); - return Storage.read(location, dataLength); + return Storage.load(location, dataLength); } /************************************************************************ @@ -497,7 +497,7 @@ library StoreCoreInternal { function _loadEncodedDynamicDataLength(bytes32 table, bytes32[] memory key) internal view returns (PackedCounter) { // Load dynamic data length from storage bytes32 dynamicSchemaLengthSlot = _getDynamicDataLengthLocation(table, key); - return PackedCounter.wrap(Storage.read(dynamicSchemaLengthSlot)); + return PackedCounter.wrap(Storage.load(dynamicSchemaLengthSlot)); } /** @@ -511,13 +511,13 @@ library StoreCoreInternal { ) internal { // Load dynamic data length from storage bytes32 dynamicSchemaLengthSlot = _getDynamicDataLengthLocation(table, key); - PackedCounter encodedLengths = PackedCounter.wrap(Storage.read(dynamicSchemaLengthSlot)); + PackedCounter encodedLengths = PackedCounter.wrap(Storage.load(dynamicSchemaLengthSlot)); // Update the encoded lengths encodedLengths = encodedLengths.setAtIndex(dynamicSchemaIndex, newLengthAtIndex); // Set the new lengths - Storage.write(dynamicSchemaLengthSlot, encodedLengths.unwrap()); + Storage.store(dynamicSchemaLengthSlot, encodedLengths.unwrap()); } } diff --git a/packages/store/src/test/Storage.t.sol b/packages/store/src/test/Storage.t.sol index 5ce0df69db..2d8c23117d 100644 --- a/packages/store/src/test/Storage.t.sol +++ b/packages/store/src/test/Storage.t.sol @@ -8,7 +8,7 @@ import { Utils } from "../Utils.sol"; import { Bytes } from "../Bytes.sol"; contract StorageTest is Test { - function testWriteRead() public { + function testStoreLoad() public { bytes memory data1 = bytes.concat( bytes1(0x01), bytes32(0x0200000000000000000000000000000000000000000000000000000000000003), @@ -25,37 +25,37 @@ contract StorageTest is Test { uint256 storagePointer = uint256(keccak256("some location")); uint256 storagePointerTwoSlotsAfter = storagePointer + 2; - // First write some data to storage at the target slot and two slots after the target slot + // First store some data to storage at the target slot and two slots after the target slot - // !gasreport write 1 storage slot - Storage.write(storagePointer, originalDataFirstSlot); + // !gasreport store 1 storage slot + Storage.store(storagePointer, originalDataFirstSlot); - Storage.write(storagePointerTwoSlotsAfter, originalDataLastSlot); + Storage.store(storagePointerTwoSlotsAfter, originalDataLastSlot); // Then set the target slot, partially overwriting the first and third slot, but using safeTrail and offset - // !gasreport write 34 bytes over 3 storage slots (with offset and safeTrail)) - Storage.write(storagePointer, 31, data1); + // !gasreport store 34 bytes over 3 storage slots (with offset and safeTrail)) + Storage.store(storagePointer, 31, data1); // Assert the first slot has the correct value - assertEq(Storage.read(storagePointer), bytes32(0x4200000000000000000000000000000000000000000000000000000000006901)); + assertEq(Storage.load(storagePointer), bytes32(0x4200000000000000000000000000000000000000000000000000000000006901)); // Assert the second slot has the correct value assertEq( - Storage.read(storagePointer + 1), + Storage.load(storagePointer + 1), bytes32(0x0200000000000000000000000000000000000000000000000000000000000003) ); // Assert that the trailing slot has the correct value assertEq( - Storage.read(storagePointerTwoSlotsAfter), + Storage.load(storagePointerTwoSlotsAfter), bytes32(0x0442000000000000000000000000000000000000000000000000000000000069) ); - // Assert we can read the correct partial value from storage + // Assert we can load the correct partial value from storage - // !gasreport read 34 bytes over 3 storage slots (with offset and safeTrail)) - bytes memory data = Storage.read(storagePointer, 31, 34); + // !gasreport load 34 bytes over 3 storage slots (with offset and safeTrail)) + bytes memory data = Storage.load(storagePointer, 31, 34); assertEq(Bytes.slice1(data, 0), bytes1(0x01)); assertEq(Bytes.slice32(data, 1), bytes32(0x0200000000000000000000000000000000000000000000000000000000000003)); @@ -63,12 +63,12 @@ contract StorageTest is Test { assertEq(keccak256(data), keccak256(data1)); } - function testWriteReadFuzzy( + function testStoreLoadFuzzy( bytes memory data, bytes32 storagePointer, uint8 offset ) public { - Storage.write(storagePointer, offset, data); - assertEq(keccak256(Storage.read(storagePointer, offset, data.length)), keccak256(data)); + Storage.store(storagePointer, offset, data); + assertEq(keccak256(Storage.load(storagePointer, offset, data.length)), keccak256(data)); } } From d11ca8226685a8edd4cf32a9de56b51cbe01e22e Mon Sep 17 00:00:00 2001 From: alvrs Date: Wed, 1 Feb 2023 21:27:31 +0100 Subject: [PATCH 62/82] build: another attempt at fixing the gas report action --- .github/workflows/gasreport.yml | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/.github/workflows/gasreport.yml b/.github/workflows/gasreport.yml index d7b291b415..bee09623c9 100644 --- a/.github/workflows/gasreport.yml +++ b/.github/workflows/gasreport.yml @@ -17,8 +17,6 @@ jobs: - name: git-checkout uses: actions/checkout@v3 - with: - ref: ${{ github.event.pull_request.head.sha }} - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 @@ -34,9 +32,6 @@ jobs: - name: Run gas report run: yarn workspace @latticexyz/store run gasreport - - name: Commit and push gas report - run: | - git config --local user.email "github-actions@github.com" - git config --local user.name "Gas report" - git commit -a -m "chore(store): update gas report" - git push + - uses: stefanzweifel/git-auto-commit-action@v4 + with: + commit_message: "test: update gas report" From 94486b63a45c801058ac6d7537994010232595a5 Mon Sep 17 00:00:00 2001 From: alvrs Date: Wed, 1 Feb 2023 20:35:25 +0000 Subject: [PATCH 63/82] test: update gas report --- packages/store/gas-report.txt | 90 ++++++++++++++++++----------------- yarn.lock | 57 ---------------------- 2 files changed, 46 insertions(+), 101 deletions(-) diff --git a/packages/store/gas-report.txt b/packages/store/gas-report.txt index 1bd5126764..5574f131f9 100644 --- a/packages/store/gas-report.txt +++ b/packages/store/gas-report.txt @@ -54,16 +54,16 @@ (src/test/Gas.t.sol) | pass abi encoded bytes to external contract [someContract.doSomethingWithBytes(abiEncoded)]: 6537 (src/test/Gas.t.sol) | pass custom encoded bytes to external contract [someContract.doSomethingWithBytes(customEncoded)]: 1342 (src/test/MixedTable.t.sol) | store Mixed struct in storage (native solidity) [testMixed = mixed]: 92016 -(src/test/MixedTable.t.sol) | register MixedTable schema [MixedTable.registerSchema()]: 32485 -(src/test/MixedTable.t.sol) | set record in MixedTable [MixedTable.set({ key: key, u32: 1, u128: 2, a32: a32, s: s })]: 114636 +(src/test/MixedTable.t.sol) | register MixedTable schema [MixedTable.registerSchema()]: 32525 +(src/test/MixedTable.t.sol) | set record in MixedTable [MixedTable.set({ key: key, u32: 1, u128: 2, a32: a32, s: s })]: 114716 (src/test/MixedTable.t.sol) | get record from MixedTable [Mixed memory mixed = MixedTable.get(key)]: 20494 (src/test/PackedCounter.t.sol) | get value at index of PackedCounter [packedCounter.atIndex(3)]: 272 (src/test/PackedCounter.t.sol) | set value at index of PackedCounter [packedCounter = packedCounter.setAtIndex(2, 5)]: 884 (src/test/PackedCounter.t.sol) | pack uint16 array into PackedCounter [PackedCounter packedCounter = PackedCounterLib.pack(counters)]: 2148 (src/test/PackedCounter.t.sol) | get total of PackedCounter [packedCounter.total()]: 33 -(src/test/RouteTable.t.sol) | register RouteTable schema [RouteTable.registerSchema()]: 30440 -(src/test/RouteTable.t.sol) | set RouteTable record [RouteTable.set(key, addr, selector, executionMode)]: 29726 -(src/test/RouteTable.t.sol) | get RouteTable record [Route memory systemEntry = RouteTable.get(key)]: 6506 +(src/test/RouteTable.t.sol) | register RouteTable schema [RouteTable.registerSchema()]: 30469 +(src/test/RouteTable.t.sol) | set RouteTable record [RouteTable.set(key, addr, selector, executionMode)]: 35317 +(src/test/RouteTable.t.sol) | get RouteTable record [Route memory systemEntry = RouteTable.get(key)]: 6548 (src/test/Schema.t.sol) | encode schema with 6 entries [SchemaLib.encode]: 6202 (src/test/Schema.t.sol) | get schema type at index [SchemaType schemaType1 = schema.atIndex(0)]: 200 (src/test/Schema.t.sol) | get number of dynamic fields from schema [uint256 num = schema.numDynamicFields()]: 80 @@ -72,47 +72,49 @@ (src/test/Schema.t.sol) | check if schema is empty (non-empty schema) [bool empty = encodedSchema.isEmpty()]: 13 (src/test/Schema.t.sol) | check if schema is empty (empty schema) [bool empty = encodedSchema.isEmpty()]: 13 (src/test/Schema.t.sol) | validate schema [encodedSchema.validate()]: 22716 -(src/test/Storage.t.sol) | write 1 storage slot [Storage.write(storagePointer, originalDataFirstSlot)]: 23509 -(src/test/Storage.t.sol) | write 34 bytes over 3 storage slots (with offset and safeTrail)) [Storage.write(storagePointer, 31, data1)]: 25749 -(src/test/Storage.t.sol) | read 34 bytes over 3 storage slots (with offset and safeTrail)) [bytes memory data = Storage.read(storagePointer, 31, 34)]: 4131 -(src/test/StoreCore.t.sol) | access non-existing record [bytes memory data1 = StoreCore.getRecord(table, key)]: 10041 -(src/test/StoreCore.t.sol) | access static field of non-existing record [bytes memory data2 = StoreCore.getField(table, key, 0)]: 4459 -(src/test/StoreCore.t.sol) | access dynamic field of non-existing record [bytes memory data3 = StoreCore.getField(table, key, 1)]: 3891 -(src/test/StoreCore.t.sol) | delete record (complex data, 3 slots) [StoreCore.deleteRecord(table, key)]: 13802 -(src/test/StoreCore.t.sol) | Check for existence of table (existent) [StoreCore.hasTable(table)]: 949 -(src/test/StoreCore.t.sol) | check for existence of table (non-existent) [StoreCore.hasTable(table2)]: 2950 -(src/test/StoreCore.t.sol) | register subscriber [StoreCore.registerOnUpdateHook(table, subscriber)]: 67055 -(src/test/StoreCore.t.sol) | set record on table with subscriber [StoreCore.setRecord(table, key, data)]: 72149 -(src/test/StoreCore.t.sol) | set field on table with subscriber [StoreCore.setField(table, key, 0, data)]: 34267 -(src/test/StoreCore.t.sol) | register subscriber [StoreCore.registerOnUpdateHook(table, subscriber)]: 67055 -(src/test/StoreCore.t.sol) | set record on table with subscriber [StoreCore.setRecord(table, key, data)]: 175065 -(src/test/StoreCore.t.sol) | set field on table with subscriber [StoreCore.setField(table, key, 1, arrayDataBytes)]: 37164 -(src/test/StoreCore.t.sol) | StoreCore: register schema [StoreCore.registerSchema(table, schema)]: 26373 -(src/test/StoreCore.t.sol) | StoreCore: get schema (warm) [Schema loadedSchema = StoreCore.getSchema(table)]: 894 -(src/test/StoreCore.t.sol) | set complex record with dynamic data (4 slots) [StoreCore.setRecord(table, key, data)]: 110840 -(src/test/StoreCore.t.sol) | get complex record with dynamic data (4 slots) [bytes memory loadedData = StoreCore.getRecord(table, key)]: 12700 +(src/test/Storage.t.sol) | store 1 storage slot [Storage.store(storagePointer, originalDataFirstSlot)]: 23509 +(src/test/Storage.t.sol) | store 34 bytes over 3 storage slots (with offset and safeTrail)) [Storage.store(storagePointer, 31, data1)]: 25749 +(src/test/Storage.t.sol) | load 34 bytes over 3 storage slots (with offset and safeTrail)) [bytes memory data = Storage.load(storagePointer, 31, 34)]: 4131 +(src/test/StoreCore.t.sol) | access non-existing record [bytes memory data1 = StoreCore.getRecord(table, key)]: 10081 +(src/test/StoreCore.t.sol) | access static field of non-existing record [bytes memory data2 = StoreCore.getField(table, key, 0)]: 4499 +(src/test/StoreCore.t.sol) | access dynamic field of non-existing record [bytes memory data3 = StoreCore.getField(table, key, 1)]: 3931 +(src/test/StoreCore.t.sol) | delete record (complex data, 3 slots) [StoreCore.deleteRecord(table, key)]: 18257 +(src/test/StoreCore.t.sol) | Check for existence of table (existent) [StoreCore.hasTable(table)]: 989 +(src/test/StoreCore.t.sol) | check for existence of table (non-existent) [StoreCore.hasTable(table2)]: 2990 +(src/test/StoreCore.t.sol) | register subscriber [StoreCore.registerHook(table, subscriber)]: 70398 +(src/test/StoreCore.t.sol) | set record on table with subscriber [StoreCore.setRecord(table, key, data)]: 72319 +(src/test/StoreCore.t.sol) | set static field on table with subscriber [StoreCore.setField(table, key, 0, data)]: 34395 +(src/test/StoreCore.t.sol) | delete record on table with subscriber [StoreCore.deleteRecord(table, key)]: 24819 +(src/test/StoreCore.t.sol) | register subscriber [StoreCore.registerHook(table, subscriber)]: 70398 +(src/test/StoreCore.t.sol) | set (dynamic) record on table with subscriber [StoreCore.setRecord(table, key, data)]: 175235 +(src/test/StoreCore.t.sol) | set (dynamic) field on table with subscriber [StoreCore.setField(table, key, 1, arrayDataBytes)]: 37168 +(src/test/StoreCore.t.sol) | delete (dynamic) record on table with subscriber [StoreCore.deleteRecord(table, key)]: 38680 +(src/test/StoreCore.t.sol) | StoreCore: register schema [StoreCore.registerSchema(table, schema)]: 26413 +(src/test/StoreCore.t.sol) | StoreCore: get schema (warm) [Schema loadedSchema = StoreCore.getSchema(table)]: 934 +(src/test/StoreCore.t.sol) | set complex record with dynamic data (4 slots) [StoreCore.setRecord(table, key, data)]: 110898 +(src/test/StoreCore.t.sol) | get complex record with dynamic data (4 slots) [bytes memory loadedData = StoreCore.getRecord(table, key)]: 12740 (src/test/StoreCore.t.sol) | compare: Set complex record with dynamic data using native solidity [testStruct = _testStruct]: 116815 (src/test/StoreCore.t.sol) | compare: Set complex record with dynamic data using abi.encode [testMapping[1234] = abi.encode(_testStruct)]: 267529 -(src/test/StoreCore.t.sol) | set dynamic length of dynamic index 0 [StoreCore._setDynamicDataLengthAtIndex(table, key, 0, 10)]: 23716 -(src/test/StoreCore.t.sol) | set dynamic length of dynamic index 1 [StoreCore._setDynamicDataLengthAtIndex(table, key, 1, 99)]: 1817 -(src/test/StoreCore.t.sol) | reduce dynamic length of dynamic index 0 [StoreCore._setDynamicDataLengthAtIndex(table, key, 0, 5)]: 1818 -(src/test/StoreCore.t.sol) | set static field (1 slot) [StoreCore.setField(table, key, 0, bytes.concat(firstDataBytes))]: 38119 -(src/test/StoreCore.t.sol) | get static field (1 slot) [bytes memory loadedData = StoreCore.getField(table, key, 0)]: 4549 -(src/test/StoreCore.t.sol) | set static field (overlap 2 slot) [StoreCore.setField(table, key, 1, bytes.concat(secondDataBytes))]: 33949 -(src/test/StoreCore.t.sol) | get static field (overlap 2 slot) [loadedData = StoreCore.getField(table, key, 1)]: 6333 -(src/test/StoreCore.t.sol) | set dynamic field (1 slot, first dynamic field) [StoreCore.setField(table, key, 2, thirdDataBytes)]: 57392 -(src/test/StoreCore.t.sol) | get dynamic field (1 slot, first dynamic field) [loadedData = StoreCore.getField(table, key, 2)]: 5308 -(src/test/StoreCore.t.sol) | set dynamic field (1 slot, second dynamic field) [StoreCore.setField(table, key, 3, fourthDataBytes)]: 35526 -(src/test/StoreCore.t.sol) | get dynamic field (1 slot, second dynamic field) [loadedData = StoreCore.getField(table, key, 3)]: 5323 -(src/test/StoreCore.t.sol) | set static record (1 slot) [StoreCore.setStaticData(table, key, data)]: 28620 -(src/test/StoreCore.t.sol) | get static record (1 slot) [bytes memory loadedData = StoreCore.getRecord(table, key, schema)]: 4098 -(src/test/StoreCore.t.sol) | set static record (2 slots) [StoreCore.setStaticData(table, key, data)]: 51561 -(src/test/StoreCore.t.sol) | get static record (2 slots) [bytes memory loadedData = StoreCore.getRecord(table, key, schema)]: 4987 +(src/test/StoreCore.t.sol) | set dynamic length of dynamic index 0 [StoreCoreInternal._setDynamicDataLengthAtIndex(table, key, 0, 10)]: 23716 +(src/test/StoreCore.t.sol) | set dynamic length of dynamic index 1 [StoreCoreInternal._setDynamicDataLengthAtIndex(table, key, 1, 99)]: 1817 +(src/test/StoreCore.t.sol) | reduce dynamic length of dynamic index 0 [StoreCoreInternal._setDynamicDataLengthAtIndex(table, key, 0, 5)]: 1818 +(src/test/StoreCore.t.sol) | set static field (1 slot) [StoreCore.setField(table, key, 0, bytes.concat(firstDataBytes))]: 38177 +(src/test/StoreCore.t.sol) | get static field (1 slot) [bytes memory loadedData = StoreCore.getField(table, key, 0)]: 4589 +(src/test/StoreCore.t.sol) | set static field (overlap 2 slot) [StoreCore.setField(table, key, 1, bytes.concat(secondDataBytes))]: 34007 +(src/test/StoreCore.t.sol) | get static field (overlap 2 slot) [loadedData = StoreCore.getField(table, key, 1)]: 6373 +(src/test/StoreCore.t.sol) | set dynamic field (1 slot, first dynamic field) [StoreCore.setField(table, key, 2, thirdDataBytes)]: 57450 +(src/test/StoreCore.t.sol) | get dynamic field (1 slot, first dynamic field) [loadedData = StoreCore.getField(table, key, 2)]: 5348 +(src/test/StoreCore.t.sol) | set dynamic field (1 slot, second dynamic field) [StoreCore.setField(table, key, 3, fourthDataBytes)]: 35584 +(src/test/StoreCore.t.sol) | get dynamic field (1 slot, second dynamic field) [loadedData = StoreCore.getField(table, key, 3)]: 5363 +(src/test/StoreCore.t.sol) | set static record (1 slot) [StoreCore.setRecord(table, key, data)]: 34213 +(src/test/StoreCore.t.sol) | get static record (1 slot) [bytes memory loadedData = StoreCore.getRecord(table, key, schema)]: 4099 +(src/test/StoreCore.t.sol) | set static record (2 slots) [StoreCore.setRecord(table, key, data)]: 56828 +(src/test/StoreCore.t.sol) | get static record (2 slots) [bytes memory loadedData = StoreCore.getRecord(table, key, schema)]: 4988 (src/test/StoreSwitch.t.sol) | check if delegatecall [isDelegate = StoreSwitch.isDelegateCall()]: 671 (src/test/StoreSwitch.t.sol) | check if delegatecall [isDelegate = StoreSwitch.isDelegateCall()]: 627 (src/test/System.t.sol) | extract msg.sender from calldata [address sender = _msgSender()]: 24 -(src/test/Vector2Table.t.sol) | register Vector2Table schema [Vector2Table.registerSchema()]: 28662 -(src/test/Vector2Table.t.sol) | set Vector2Table record [Vector2Table.set({ key: key, x: 1, y: 2 })]: 29689 -(src/test/Vector2Table.t.sol) | get Vector2Table record [Vector2 memory vector = Vector2Table.get(key)]: 6391 -(src/test/World.t.sol) | call autonomous system via World contract [WorldWithTestSystem(address(world)).TestSystem_move(entity, 1, 2)]: 41998 -(src/test/World.t.sol) | call delegate system via World contract [WorldWithTestSystem(address(world)).TestSystem_move(entity, 1, 2)]: 39940 \ No newline at end of file +(src/test/Vector2Table.t.sol) | register Vector2Table schema [Vector2Table.registerSchema()]: 28702 +(src/test/Vector2Table.t.sol) | set Vector2Table record [Vector2Table.set({ key: key, x: 1, y: 2 })]: 35283 +(src/test/Vector2Table.t.sol) | get Vector2Table record [Vector2 memory vector = Vector2Table.get(key)]: 6434 +(src/test/World.t.sol) | call autonomous system via World contract [WorldWithWorldTestSystem(address(world)).WorldTestSystem_move(entity, 1, 2)]: 45582 +(src/test/World.t.sol) | call delegate system via World contract [WorldWithWorldTestSystem(address(world)).WorldTestSystem_move(entity, 1, 2)]: 43447 \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 49db7ada7c..cbf2439125 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1703,63 +1703,6 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@latticexyz/cli@^1.34.0": - version "1.34.0" - resolved "https://registry.yarnpkg.com/@latticexyz/cli/-/cli-1.34.0.tgz#df43fe98723117d4b0bcc13bfe9b8e2740e05dcc" - integrity sha512-lZt4nkUuAmR7Q2y5RvFR+vYGAJG+U4iKPrvhD/9pXNmsl+wbR06Pe3+aGcuMWjviV04gY6xdER95D3F95O5uiQ== - dependencies: - "@latticexyz/services" "^1.34.0" - "@latticexyz/solecs" "^1.34.0" - "@latticexyz/std-contracts" "^1.34.0" - "@latticexyz/utils" "^1.34.0" - "@typechain/ethers-v5" "^10.1.1" - chalk "^5.0.1" - chokidar "^3.5.3" - clear "^0.1.0" - commander "^9.2.0" - ds-test "https://github.com/dapphub/ds-test.git#c9ce3f25bde29fc5eb9901842bf02850dfd2d084" - ejs "^3.1.8" - esm "^3.2.25" - ethers "^5.7.2" - execa "^6.1.0" - figlet "^1.5.2" - forge-std "https://github.com/foundry-rs/forge-std.git#f36dab24d63d1c1945a05ed375ce341d3c1a49ed" - glob "^8.0.3" - inquirer "^8.2.4" - inquirer-prompt-suggest "^0.1.0" - listr2 "^4.0.5" - netlify "^11.0.1" - nice-grpc-web "^2.0.1" - node-fetch "^3.2.6" - openurl "^1.1.1" - path "^0.12.7" - solmate "https://github.com/Rari-Capital/solmate.git#9cf1428245074e39090dceacb0c28b1f684f584c" - typechain "^8.1.1" - uuid "^8.3.2" - yargs "^17.5.1" - -"@latticexyz/services@^1.34.0": - version "1.34.0" - resolved "https://registry.yarnpkg.com/@latticexyz/services/-/services-1.34.0.tgz#3085d391c54f845a7a17f78ba874592f5651d80d" - integrity sha512-0oqk9ys1JxnlKIbzp+zsdQ59p0CLWm4zRZsx83GjJKb/9XiiCo26/GDrV00ALHAEtRU4dCrP9mbmaY8zrCOc5w== - -"@latticexyz/solecs@^1.34.0": - version "1.34.0" - resolved "https://registry.yarnpkg.com/@latticexyz/solecs/-/solecs-1.34.0.tgz#bfbf6ca5b577f5c5f5912a35704d99c1ecc089e9" - integrity sha512-tgUyHJWjs03PeOiqxzc+P98NUv2GcH+Ml87puhb/7RFv8WLrus7QOjkiwUcHHRO6+UZCBwr34ySaCY3AzJ4nUg== - -"@latticexyz/std-contracts@^1.34.0": - version "1.34.0" - resolved "https://registry.yarnpkg.com/@latticexyz/std-contracts/-/std-contracts-1.34.0.tgz#3f2bdae721d7aaa641f94ca83592592ab60055f8" - integrity sha512-eLX6qLQV+wQoJpkcO9OFFYB/uJq1ih+hZ1Ufg1n5NzOGzf3kWqJTwQwtSaYWDwUe9bszw2FtreivyqmwfYwubw== - -"@latticexyz/utils@^1.34.0": - version "1.34.0" - resolved "https://registry.yarnpkg.com/@latticexyz/utils/-/utils-1.34.0.tgz#90d98ac11eb3da8e9aa81af686f0f41151119d74" - integrity sha512-tr/LV4QfhV0XYjtrY8DHLYEI2EPHzVQXmK1JtjupstLvK31WpE9xvgIqMYTawxupC9xw2c5q6zxEs/JZtiUmSQ== - dependencies: - typedoc-plugin-markdown "^3.13.6" - "@lerna/add@4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@lerna/add/-/add-4.0.0.tgz#c36f57d132502a57b9e7058d1548b7a565ef183f" From 6053be6783781df44620642183909b01ff7af37a Mon Sep 17 00:00:00 2001 From: alvrs Date: Wed, 1 Feb 2023 22:01:44 +0100 Subject: [PATCH 64/82] refactor(store): move test out of src --- packages/store/package.json | 2 +- packages/store/{src => }/test/Buffer.t.sol | 4 +-- packages/store/{src => }/test/Bytes.t.sol | 2 +- packages/store/{src => }/test/Gas.t.sol | 4 +-- .../store/{src => }/test/MixedTable.t.sol | 10 +++---- .../store/{src => }/test/PackedCounter.t.sol | 2 +- .../store/{src => }/test/RouteTable.t.sol | 10 +++---- packages/store/{src => }/test/Schema.t.sol | 4 +-- packages/store/{src => }/test/Storage.t.sol | 8 +++--- packages/store/{src => }/test/StoreCore.t.sol | 26 +++++++++---------- .../store/{src => }/test/StoreSwitch.t.sol | 8 +++--- packages/store/{src => }/test/System.t.sol | 4 +-- .../store/{src => }/test/Vector2Table.t.sol | 10 +++---- packages/store/{src => }/test/World.t.sol | 8 +++--- .../{src => }/test/schemas/AddressArray.t.sol | 4 +-- .../test/schemas/CallbackArray.t.sol | 4 +-- 16 files changed, 55 insertions(+), 55 deletions(-) rename packages/store/{src => }/test/Buffer.t.sol (98%) rename packages/store/{src => }/test/Bytes.t.sol (99%) rename packages/store/{src => }/test/Gas.t.sol (95%) rename packages/store/{src => }/test/MixedTable.t.sol (82%) rename packages/store/{src => }/test/PackedCounter.t.sol (95%) rename packages/store/{src => }/test/RouteTable.t.sol (78%) rename packages/store/{src => }/test/Schema.t.sol (98%) rename packages/store/{src => }/test/Storage.t.sol (93%) rename packages/store/{src => }/test/StoreCore.t.sol (96%) rename packages/store/{src => }/test/StoreSwitch.t.sol (87%) rename packages/store/{src => }/test/System.t.sol (88%) rename packages/store/{src => }/test/Vector2Table.t.sol (80%) rename packages/store/{src => }/test/World.t.sol (94%) rename packages/store/{src => }/test/schemas/AddressArray.t.sol (88%) rename packages/store/{src => }/test/schemas/CallbackArray.t.sol (88%) diff --git a/packages/store/package.json b/packages/store/package.json index 3808c29008..53fa3d3b26 100644 --- a/packages/store/package.json +++ b/packages/store/package.json @@ -22,7 +22,7 @@ "link": "yarn link", "docs": "rimraf API && hardhat docgen && echo 'label: API\norder: 50' > API/index.yml", "release": "npm publish || echo 'version already published'", - "gasreport": " ../cli/dist/index.js gas-report --path src/test/** --save gas-report.txt" + "gasreport": " ../cli/dist/index.js gas-report --path test/** --save gas-report.txt" }, "devDependencies": { "@typechain/ethers-v5": "^9.0.0", diff --git a/packages/store/src/test/Buffer.t.sol b/packages/store/test/Buffer.t.sol similarity index 98% rename from packages/store/src/test/Buffer.t.sol rename to packages/store/test/Buffer.t.sol index b2a7c92449..80f17f5502 100644 --- a/packages/store/src/test/Buffer.t.sol +++ b/packages/store/test/Buffer.t.sol @@ -2,8 +2,8 @@ pragma solidity >=0.8.0; import "forge-std/Test.sol"; -import { Cast } from "../Cast.sol"; -import "../Buffer.sol"; +import { Cast } from "../src/Cast.sol"; +import "../src/Buffer.sol"; contract BufferTest is Test { function testAllocateBuffer() public { diff --git a/packages/store/src/test/Bytes.t.sol b/packages/store/test/Bytes.t.sol similarity index 99% rename from packages/store/src/test/Bytes.t.sol rename to packages/store/test/Bytes.t.sol index 25fb95d272..26f867d7ba 100644 --- a/packages/store/src/test/Bytes.t.sol +++ b/packages/store/test/Bytes.t.sol @@ -2,7 +2,7 @@ pragma solidity >=0.8.0; import "forge-std/Test.sol"; -import { Bytes } from "../Bytes.sol"; +import { Bytes } from "../src/Bytes.sol"; contract BytesTest is Test { function testFromBytesArray() public { diff --git a/packages/store/src/test/Gas.t.sol b/packages/store/test/Gas.t.sol similarity index 95% rename from packages/store/src/test/Gas.t.sol rename to packages/store/test/Gas.t.sol index 50a481994f..01d2a96f8e 100644 --- a/packages/store/src/test/Gas.t.sol +++ b/packages/store/test/Gas.t.sol @@ -2,8 +2,8 @@ pragma solidity >=0.8.0; import "forge-std/Test.sol"; -import { Bytes } from "../Bytes.sol"; -import { Buffer_ } from "../Buffer.sol"; +import { Bytes } from "../src/Bytes.sol"; +import { Buffer_ } from "../src/Buffer.sol"; struct Mixed { uint32 u32; diff --git a/packages/store/src/test/MixedTable.t.sol b/packages/store/test/MixedTable.t.sol similarity index 82% rename from packages/store/src/test/MixedTable.t.sol rename to packages/store/test/MixedTable.t.sol index 0c7597d44c..68565d18bc 100644 --- a/packages/store/src/test/MixedTable.t.sol +++ b/packages/store/test/MixedTable.t.sol @@ -2,11 +2,11 @@ pragma solidity >=0.8.0; import "forge-std/Test.sol"; -import { MixedTable, tableId as MixedTableId, Mixed } from "../tables/MixedTable.sol"; -import { StoreCore } from "../StoreCore.sol"; -import { SchemaType } from "../Types.sol"; -import { StoreView } from "../StoreView.sol"; -import { Schema } from "../Schema.sol"; +import { MixedTable, tableId as MixedTableId, Mixed } from "../src/tables/MixedTable.sol"; +import { StoreCore } from "../src/StoreCore.sol"; +import { SchemaType } from "../src/Types.sol"; +import { StoreView } from "../src/StoreView.sol"; +import { Schema } from "../src/Schema.sol"; contract MixedTableTest is Test, StoreView { Mixed private testMixed; diff --git a/packages/store/src/test/PackedCounter.t.sol b/packages/store/test/PackedCounter.t.sol similarity index 95% rename from packages/store/src/test/PackedCounter.t.sol rename to packages/store/test/PackedCounter.t.sol index 9aeb468f6d..8ec1938f73 100644 --- a/packages/store/src/test/PackedCounter.t.sol +++ b/packages/store/test/PackedCounter.t.sol @@ -2,7 +2,7 @@ pragma solidity >=0.8.0; import "forge-std/Test.sol"; -import { PackedCounter, PackedCounterLib } from "../PackedCounter.sol"; +import { PackedCounter, PackedCounterLib } from "../src/PackedCounter.sol"; contract PackedCounterTest is Test { function testTotal() public { diff --git a/packages/store/src/test/RouteTable.t.sol b/packages/store/test/RouteTable.t.sol similarity index 78% rename from packages/store/src/test/RouteTable.t.sol rename to packages/store/test/RouteTable.t.sol index a701fe51ed..e7362666d4 100644 --- a/packages/store/src/test/RouteTable.t.sol +++ b/packages/store/test/RouteTable.t.sol @@ -2,11 +2,11 @@ pragma solidity >=0.8.0; import "forge-std/Test.sol"; -import { RouteTable, tableId as RouteTableId, Route } from "../tables/RouteTable.sol"; -import { StoreCore } from "../StoreCore.sol"; -import { SchemaType } from "../Types.sol"; -import { StoreView } from "../StoreView.sol"; -import { Schema } from "../Schema.sol"; +import { RouteTable, tableId as RouteTableId, Route } from "../src/tables/RouteTable.sol"; +import { StoreCore } from "../src/StoreCore.sol"; +import { SchemaType } from "../src/Types.sol"; +import { StoreView } from "../src/StoreView.sol"; +import { Schema } from "../src/Schema.sol"; contract RouteTableTest is Test, StoreView { function testRegisterAndGetSchema() public { diff --git a/packages/store/src/test/Schema.t.sol b/packages/store/test/Schema.t.sol similarity index 98% rename from packages/store/src/test/Schema.t.sol rename to packages/store/test/Schema.t.sol index f63feb446c..be6c59611d 100644 --- a/packages/store/src/test/Schema.t.sol +++ b/packages/store/test/Schema.t.sol @@ -2,8 +2,8 @@ pragma solidity >=0.8.0; import "forge-std/Test.sol"; -import { Schema, SchemaLib } from "../Schema.sol"; -import { SchemaType } from "../Types.sol"; +import { Schema, SchemaLib } from "../src/Schema.sol"; +import { SchemaType } from "../src/Types.sol"; contract SchemaTest is Test { function testEncodeDecodeSchema() public { diff --git a/packages/store/src/test/Storage.t.sol b/packages/store/test/Storage.t.sol similarity index 93% rename from packages/store/src/test/Storage.t.sol rename to packages/store/test/Storage.t.sol index 2d8c23117d..19d992b6a7 100644 --- a/packages/store/src/test/Storage.t.sol +++ b/packages/store/test/Storage.t.sol @@ -2,10 +2,10 @@ pragma solidity >=0.8.0; import "forge-std/Test.sol"; -import { Cast } from "../Cast.sol"; -import { Storage } from "../Storage.sol"; -import { Utils } from "../Utils.sol"; -import { Bytes } from "../Bytes.sol"; +import { Cast } from "../src/Cast.sol"; +import { Storage } from "../src/Storage.sol"; +import { Utils } from "../src/Utils.sol"; +import { Bytes } from "../src/Bytes.sol"; contract StorageTest is Test { function testStoreLoad() public { diff --git a/packages/store/src/test/StoreCore.t.sol b/packages/store/test/StoreCore.t.sol similarity index 96% rename from packages/store/src/test/StoreCore.t.sol rename to packages/store/test/StoreCore.t.sol index 067d5c3b7d..cd17e4cf45 100644 --- a/packages/store/src/test/StoreCore.t.sol +++ b/packages/store/test/StoreCore.t.sol @@ -2,19 +2,19 @@ pragma solidity >=0.8.0; import "forge-std/Test.sol"; -import { StoreCore, StoreCoreInternal } from "../StoreCore.sol"; -import { Utils } from "../Utils.sol"; -import { Bytes } from "../Bytes.sol"; -import { SchemaType } from "../Types.sol"; -import { Storage } from "../Storage.sol"; -import { Memory } from "../Memory.sol"; -import { Cast } from "../Cast.sol"; -import { Buffer, Buffer_ } from "../Buffer.sol"; -import { Schema, SchemaLib } from "../Schema.sol"; -import { PackedCounter, PackedCounterLib } from "../PackedCounter.sol"; -import { StoreView } from "../StoreView.sol"; -import { IStore, IStoreHook } from "../IStore.sol"; -import { StoreSwitch } from "../StoreSwitch.sol"; +import { StoreCore, StoreCoreInternal } from "../src/StoreCore.sol"; +import { Utils } from "../src/Utils.sol"; +import { Bytes } from "../src/Bytes.sol"; +import { SchemaType } from "../src/Types.sol"; +import { Storage } from "../src/Storage.sol"; +import { Memory } from "../src/Memory.sol"; +import { Cast } from "../src/Cast.sol"; +import { Buffer, Buffer_ } from "../src/Buffer.sol"; +import { Schema, SchemaLib } from "../src/Schema.sol"; +import { PackedCounter, PackedCounterLib } from "../src/PackedCounter.sol"; +import { StoreView } from "../src/StoreView.sol"; +import { IStore, IStoreHook } from "../src/IStore.sol"; +import { StoreSwitch } from "../src/StoreSwitch.sol"; struct TestStruct { uint128 firstData; diff --git a/packages/store/src/test/StoreSwitch.t.sol b/packages/store/test/StoreSwitch.t.sol similarity index 87% rename from packages/store/src/test/StoreSwitch.t.sol rename to packages/store/test/StoreSwitch.t.sol index f7de358a91..161f1f06cf 100644 --- a/packages/store/src/test/StoreSwitch.t.sol +++ b/packages/store/test/StoreSwitch.t.sol @@ -2,10 +2,10 @@ pragma solidity >=0.8.0; import "forge-std/Test.sol"; -import { SchemaType } from "../Types.sol"; -import { StoreCore } from "../StoreCore.sol"; -import { StoreView } from "../StoreView.sol"; -import { StoreSwitch } from "../StoreSwitch.sol"; +import { SchemaType } from "../src/Types.sol"; +import { StoreCore } from "../src/StoreCore.sol"; +import { StoreView } from "../src/StoreView.sol"; +import { StoreSwitch } from "../src/StoreSwitch.sol"; // Mock Store to call MockSystem contract Store is StoreView { diff --git a/packages/store/src/test/System.t.sol b/packages/store/test/System.t.sol similarity index 88% rename from packages/store/src/test/System.t.sol rename to packages/store/test/System.t.sol index fffad45ef6..0a54d5b7cf 100644 --- a/packages/store/src/test/System.t.sol +++ b/packages/store/test/System.t.sol @@ -2,8 +2,8 @@ pragma solidity >=0.8.0; import "forge-std/Test.sol"; -import { World } from "../World.sol"; -import { System } from "../System.sol"; +import { World } from "../src/World.sol"; +import { System } from "../src/System.sol"; contract TestSystem is System { function msgSender() public pure returns (address) { diff --git a/packages/store/src/test/Vector2Table.t.sol b/packages/store/test/Vector2Table.t.sol similarity index 80% rename from packages/store/src/test/Vector2Table.t.sol rename to packages/store/test/Vector2Table.t.sol index 3e6cc98b99..29f6632fad 100644 --- a/packages/store/src/test/Vector2Table.t.sol +++ b/packages/store/test/Vector2Table.t.sol @@ -2,11 +2,11 @@ pragma solidity >=0.8.0; import "forge-std/Test.sol"; -import { Vector2Table, tableId as Vector2Id, Vector2 } from "../tables/Vector2Table.sol"; -import { StoreCore } from "../StoreCore.sol"; -import { SchemaType } from "../Types.sol"; -import { StoreView } from "../StoreView.sol"; -import { Schema } from "../Schema.sol"; +import { Vector2Table, tableId as Vector2Id, Vector2 } from "../src/tables/Vector2Table.sol"; +import { StoreCore } from "../src/StoreCore.sol"; +import { SchemaType } from "../src/Types.sol"; +import { StoreView } from "../src/StoreView.sol"; +import { Schema } from "../src/Schema.sol"; contract Vector2TableTest is Test, StoreView { function testRegisterAndGetSchema() public { diff --git a/packages/store/src/test/World.t.sol b/packages/store/test/World.t.sol similarity index 94% rename from packages/store/src/test/World.t.sol rename to packages/store/test/World.t.sol index fd456ca026..f768fc496a 100644 --- a/packages/store/src/test/World.t.sol +++ b/packages/store/test/World.t.sol @@ -2,10 +2,10 @@ pragma solidity >=0.8.0; import "forge-std/Test.sol"; -import { World } from "../World.sol"; -import { System } from "../System.sol"; -import { ExecutionMode } from "../Types.sol"; -import { Vector2Table, Vector2 } from "../tables/Vector2Table.sol"; +import { World } from "../src/World.sol"; +import { System } from "../src/System.sol"; +import { ExecutionMode } from "../src/Types.sol"; +import { Vector2Table, Vector2 } from "../src/tables/Vector2Table.sol"; contract WorldTestSystem is System { function msgSender() public pure returns (address) { diff --git a/packages/store/src/test/schemas/AddressArray.t.sol b/packages/store/test/schemas/AddressArray.t.sol similarity index 88% rename from packages/store/src/test/schemas/AddressArray.t.sol rename to packages/store/test/schemas/AddressArray.t.sol index 0fc5cf59e3..10c97ce7f8 100644 --- a/packages/store/src/test/schemas/AddressArray.t.sol +++ b/packages/store/test/schemas/AddressArray.t.sol @@ -2,8 +2,8 @@ pragma solidity >=0.8.0; import "forge-std/Test.sol"; -import { StoreView } from "../../StoreView.sol"; -import { AddressArray, AddressArray_ } from "../../schemas/AddressArray.sol"; +import { StoreView } from "../../src/StoreView.sol"; +import { AddressArray, AddressArray_ } from "../../src/schemas/AddressArray.sol"; bytes32 constant tableId = keccak256("mud.store.table.addressArray"); diff --git a/packages/store/src/test/schemas/CallbackArray.t.sol b/packages/store/test/schemas/CallbackArray.t.sol similarity index 88% rename from packages/store/src/test/schemas/CallbackArray.t.sol rename to packages/store/test/schemas/CallbackArray.t.sol index 90b5d5ecdc..7e52465a3f 100644 --- a/packages/store/src/test/schemas/CallbackArray.t.sol +++ b/packages/store/test/schemas/CallbackArray.t.sol @@ -2,8 +2,8 @@ pragma solidity >=0.8.0; import "forge-std/Test.sol"; -import { StoreView } from "../../StoreView.sol"; -import { CallbackArray, CallbackArray_ } from "../../schemas/CallbackArray.sol"; +import { StoreView } from "../../src/StoreView.sol"; +import { CallbackArray, CallbackArray_ } from "../../src/schemas/CallbackArray.sol"; bytes32 constant tableId = keccak256("mud.store.table.callbackArray"); From 1fd8d6a661954f1161b6e233e011fe53bf4829f4 Mon Sep 17 00:00:00 2001 From: alvrs Date: Wed, 1 Feb 2023 21:10:33 +0000 Subject: [PATCH 65/82] test: update gas report --- packages/store/gas-report.txt | 240 +++++++++++++++++----------------- 1 file changed, 120 insertions(+), 120 deletions(-) diff --git a/packages/store/gas-report.txt b/packages/store/gas-report.txt index 5574f131f9..14314c6445 100644 --- a/packages/store/gas-report.txt +++ b/packages/store/gas-report.txt @@ -1,120 +1,120 @@ -(src/test/Buffer.t.sol) | allocate a buffer [Buffer buf = Buffer_.allocate(32)]: 73 -(src/test/Buffer.t.sol) | get buffer length [buf.length()]: 87 -(src/test/Buffer.t.sol) | get buffer pointer [buf.ptr()]: 33 -(src/test/Buffer.t.sol) | get buffer capacity [buf.capacity()]: 7 -(src/test/Buffer.t.sol) | append unchecked bytes memory (8) to buffer [buf.appendUnchecked(data1)]: 478 -(src/test/Buffer.t.sol) | append bytes memory (8) to buffer [buf.append(data2)]: 773 -(src/test/Buffer.t.sol) | append unchecked bytes8 of bytes32 to buffer [buf.appendUnchecked(data1, 8)]: 351 -(src/test/Buffer.t.sol) | append bytes8 of bytes32 to buffer [buf.append(data2, 8)]: 645 -(src/test/Buffer.t.sol) | concat 3 bytes memory (32) using buffer [Buffer buf = Buffer_.concat(data1, data2, data3)]: 2638 -(src/test/Buffer.t.sol) | concat 3 bytes memory (32) using bytes.concat [bytes memory concat = bytes.concat(data1, data2, data3)]: 641 -(src/test/Buffer.t.sol) | create a buffer from 8 bytes [Buffer buf = Buffer_.fromBytes(data)]: 40 -(src/test/Buffer.t.sol) | read bytes32 from buffer [bytes32 value = buf.read32(4)]: 102 -(src/test/Buffer.t.sol) | read bytes8 with offset 3 from buffer [bytes8 value2 = buf.read8(3)]: 151 -(src/test/Buffer.t.sol) | read bytes1 with offset 7 from buffer [bytes1 value3 = buf.read1(7)]: 151 -(src/test/Buffer.t.sol) | set buffer length unchecked [buf._setLengthUnchecked(8)]: 86 -(src/test/Buffer.t.sol) | set buffer length [buf._setLength(16)]: 192 -(src/test/Buffer.t.sol) | slice 4 bytes from buffer with offset 4 [bytes memory slice = buf.slice(4, 4)]: 635 -(src/test/Buffer.t.sol) | convert array pointer to uint256[] [uint256[] memory arr = Cast.toUint256Array(arrayPtr)]: 10 -(src/test/Buffer.t.sol) | buffer toArray with element length 4 [uint256 arrayPtr = buf.toArray(4)]: 745 -(src/test/Buffer.t.sol) | convert array pointer to uint32[] [uint32[] memory arr = Cast.toUint32Array(arrayPtr)]: 10 -(src/test/Buffer.t.sol) | buffer (32 bytes) to bytes memory [bytes memory bufferData = buf.toBytes()]: 90 -(src/test/Bytes.t.sol) | compare equal bytes [bool equals = Bytes.equals(a, b)]: 202 -(src/test/Bytes.t.sol) | compare unequal bytes [bool equals = Bytes.equals(a, b)]: 202 -(src/test/Bytes.t.sol) | create bytes from address [bytes memory output = Bytes.from(input)]: 157 -(src/test/Bytes.t.sol) | create address from bytes [address output2 = Bytes.toAddress(output)]: 166 -(src/test/Bytes.t.sol) | create bytes from bytes4 [bytes memory output = Bytes.from(input)]: 151 -(src/test/Bytes.t.sol) | create bytes4 from bytes [bytes4 output2 = Bytes.toBytes4(output)]: 160 -(src/test/Bytes.t.sol) | create bytes from uint32 [bytes memory output = Bytes.from(input)]: 149 -(src/test/Bytes.t.sol) | create uint32 from bytes [uint32 output2 = Bytes.toUint32(output)]: 166 -(src/test/Bytes.t.sol) | create uint32 array from bytes memory [uint32[] memory output = Bytes.toUint32Array(tight)]: 835 -(src/test/Bytes.t.sol) | create bytes from uint8 [bytes memory output = Bytes.fromUint8(input)]: 160 -(src/test/Bytes.t.sol) | create uint8 from bytes [uint8 output2 = Bytes.toUint8(output)]: 105 -(src/test/Bytes.t.sol) | create bytes from bytes array [bytes memory output = Bytes.from(input)]: 1290 -(src/test/Bytes.t.sol) | create bytes from uint16 array [bytes memory output = Bytes.from(input)]: 791 -(src/test/Bytes.t.sol) | create bytes from uint32 array [bytes memory output = Bytes.from(input)]: 695 -(src/test/Bytes.t.sol) | create bytes from uint8 array [bytes memory output = Bytes.from(input)]: 695 -(src/test/Bytes.t.sol) | set bytes1 in bytes32 [Bytes.setBytes1(input, 8, 0xff)]: 7 -(src/test/Bytes.t.sol) | set bytes2 in bytes32 [Bytes.setBytes2(input, 8, 0xffff)]: 7 -(src/test/Bytes.t.sol) | set bytes4 in bytes32 [Bytes.setBytes4(input, 8, 0xffffffff)]: 7 -(src/test/Bytes.t.sol) | set length of bytes in place [Bytes.setLengthInPlace(a, 2)]: 16 -(src/test/Bytes.t.sol) | slice bytes (with copying) with offset 1 and length 3 [bytes memory b = Bytes.slice(a, 1, 3)]: 516 -(src/test/Bytes.t.sol) | slice bytes3 with offset 1 [bytes3 b = Bytes.slice3(a, 1)]: 77 -(src/test/Bytes.t.sol) | slice bytes32 with offset 10 [bytes32 output = Bytes.slice32(input, 10)]: 74 -(src/test/Bytes.t.sol) | tightly pack bytes24 array into bytes array [bytes memory tight = Bytes.from(input)]: 477 -(src/test/Bytes.t.sol) | create uint32 array from bytes memory [bytes24[] memory output = Bytes.toBytes24Array(tight)]: 614 -(src/test/Bytes.t.sol) | create bytes32 from bytes memory with offset 0 [bytes32 output = Bytes.toBytes32(input, 0)]: 22 -(src/test/Bytes.t.sol) | create bytes32 array from bytes memory [bytes32[] memory output = Bytes.toBytes32Array(input)]: 1110 -(src/test/Bytes.t.sol) | create bytes32 array from bytes memory with uneven length [bytes32[] memory output = Bytes.toBytes32Array(input)]: 1425 -(src/test/Bytes.t.sol) | create bytes32 from bytes memory with offset 16 [bytes32 output = Bytes.toBytes32(input, 16)]: 22 -(src/test/Gas.t.sol) | abi encode [bytes memory abiEncoded = abi.encode(mixed)]: 930 -(src/test/Gas.t.sol) | abi decode [Mixed memory abiDecoded = abi.decode(abiEncoded, (Mixed))]: 1713 -(src/test/Gas.t.sol) | custom encode [bytes memory customEncoded = customEncode(mixed)]: 1393 -(src/test/Gas.t.sol) | custom decode [Mixed memory customDecoded = customDecode(customEncoded)]: 2772 -(src/test/Gas.t.sol) | pass abi encoded bytes to external contract [someContract.doSomethingWithBytes(abiEncoded)]: 6537 -(src/test/Gas.t.sol) | pass custom encoded bytes to external contract [someContract.doSomethingWithBytes(customEncoded)]: 1342 -(src/test/MixedTable.t.sol) | store Mixed struct in storage (native solidity) [testMixed = mixed]: 92016 -(src/test/MixedTable.t.sol) | register MixedTable schema [MixedTable.registerSchema()]: 32525 -(src/test/MixedTable.t.sol) | set record in MixedTable [MixedTable.set({ key: key, u32: 1, u128: 2, a32: a32, s: s })]: 114716 -(src/test/MixedTable.t.sol) | get record from MixedTable [Mixed memory mixed = MixedTable.get(key)]: 20494 -(src/test/PackedCounter.t.sol) | get value at index of PackedCounter [packedCounter.atIndex(3)]: 272 -(src/test/PackedCounter.t.sol) | set value at index of PackedCounter [packedCounter = packedCounter.setAtIndex(2, 5)]: 884 -(src/test/PackedCounter.t.sol) | pack uint16 array into PackedCounter [PackedCounter packedCounter = PackedCounterLib.pack(counters)]: 2148 -(src/test/PackedCounter.t.sol) | get total of PackedCounter [packedCounter.total()]: 33 -(src/test/RouteTable.t.sol) | register RouteTable schema [RouteTable.registerSchema()]: 30469 -(src/test/RouteTable.t.sol) | set RouteTable record [RouteTable.set(key, addr, selector, executionMode)]: 35317 -(src/test/RouteTable.t.sol) | get RouteTable record [Route memory systemEntry = RouteTable.get(key)]: 6548 -(src/test/Schema.t.sol) | encode schema with 6 entries [SchemaLib.encode]: 6202 -(src/test/Schema.t.sol) | get schema type at index [SchemaType schemaType1 = schema.atIndex(0)]: 200 -(src/test/Schema.t.sol) | get number of dynamic fields from schema [uint256 num = schema.numDynamicFields()]: 80 -(src/test/Schema.t.sol) | get number of static fields from schema [uint256 num = schema.numStaticFields()]: 91 -(src/test/Schema.t.sol) | get static data length from schema [uint256 length = schema.staticDataLength()]: 39 -(src/test/Schema.t.sol) | check if schema is empty (non-empty schema) [bool empty = encodedSchema.isEmpty()]: 13 -(src/test/Schema.t.sol) | check if schema is empty (empty schema) [bool empty = encodedSchema.isEmpty()]: 13 -(src/test/Schema.t.sol) | validate schema [encodedSchema.validate()]: 22716 -(src/test/Storage.t.sol) | store 1 storage slot [Storage.store(storagePointer, originalDataFirstSlot)]: 23509 -(src/test/Storage.t.sol) | store 34 bytes over 3 storage slots (with offset and safeTrail)) [Storage.store(storagePointer, 31, data1)]: 25749 -(src/test/Storage.t.sol) | load 34 bytes over 3 storage slots (with offset and safeTrail)) [bytes memory data = Storage.load(storagePointer, 31, 34)]: 4131 -(src/test/StoreCore.t.sol) | access non-existing record [bytes memory data1 = StoreCore.getRecord(table, key)]: 10081 -(src/test/StoreCore.t.sol) | access static field of non-existing record [bytes memory data2 = StoreCore.getField(table, key, 0)]: 4499 -(src/test/StoreCore.t.sol) | access dynamic field of non-existing record [bytes memory data3 = StoreCore.getField(table, key, 1)]: 3931 -(src/test/StoreCore.t.sol) | delete record (complex data, 3 slots) [StoreCore.deleteRecord(table, key)]: 18257 -(src/test/StoreCore.t.sol) | Check for existence of table (existent) [StoreCore.hasTable(table)]: 989 -(src/test/StoreCore.t.sol) | check for existence of table (non-existent) [StoreCore.hasTable(table2)]: 2990 -(src/test/StoreCore.t.sol) | register subscriber [StoreCore.registerHook(table, subscriber)]: 70398 -(src/test/StoreCore.t.sol) | set record on table with subscriber [StoreCore.setRecord(table, key, data)]: 72319 -(src/test/StoreCore.t.sol) | set static field on table with subscriber [StoreCore.setField(table, key, 0, data)]: 34395 -(src/test/StoreCore.t.sol) | delete record on table with subscriber [StoreCore.deleteRecord(table, key)]: 24819 -(src/test/StoreCore.t.sol) | register subscriber [StoreCore.registerHook(table, subscriber)]: 70398 -(src/test/StoreCore.t.sol) | set (dynamic) record on table with subscriber [StoreCore.setRecord(table, key, data)]: 175235 -(src/test/StoreCore.t.sol) | set (dynamic) field on table with subscriber [StoreCore.setField(table, key, 1, arrayDataBytes)]: 37168 -(src/test/StoreCore.t.sol) | delete (dynamic) record on table with subscriber [StoreCore.deleteRecord(table, key)]: 38680 -(src/test/StoreCore.t.sol) | StoreCore: register schema [StoreCore.registerSchema(table, schema)]: 26413 -(src/test/StoreCore.t.sol) | StoreCore: get schema (warm) [Schema loadedSchema = StoreCore.getSchema(table)]: 934 -(src/test/StoreCore.t.sol) | set complex record with dynamic data (4 slots) [StoreCore.setRecord(table, key, data)]: 110898 -(src/test/StoreCore.t.sol) | get complex record with dynamic data (4 slots) [bytes memory loadedData = StoreCore.getRecord(table, key)]: 12740 -(src/test/StoreCore.t.sol) | compare: Set complex record with dynamic data using native solidity [testStruct = _testStruct]: 116815 -(src/test/StoreCore.t.sol) | compare: Set complex record with dynamic data using abi.encode [testMapping[1234] = abi.encode(_testStruct)]: 267529 -(src/test/StoreCore.t.sol) | set dynamic length of dynamic index 0 [StoreCoreInternal._setDynamicDataLengthAtIndex(table, key, 0, 10)]: 23716 -(src/test/StoreCore.t.sol) | set dynamic length of dynamic index 1 [StoreCoreInternal._setDynamicDataLengthAtIndex(table, key, 1, 99)]: 1817 -(src/test/StoreCore.t.sol) | reduce dynamic length of dynamic index 0 [StoreCoreInternal._setDynamicDataLengthAtIndex(table, key, 0, 5)]: 1818 -(src/test/StoreCore.t.sol) | set static field (1 slot) [StoreCore.setField(table, key, 0, bytes.concat(firstDataBytes))]: 38177 -(src/test/StoreCore.t.sol) | get static field (1 slot) [bytes memory loadedData = StoreCore.getField(table, key, 0)]: 4589 -(src/test/StoreCore.t.sol) | set static field (overlap 2 slot) [StoreCore.setField(table, key, 1, bytes.concat(secondDataBytes))]: 34007 -(src/test/StoreCore.t.sol) | get static field (overlap 2 slot) [loadedData = StoreCore.getField(table, key, 1)]: 6373 -(src/test/StoreCore.t.sol) | set dynamic field (1 slot, first dynamic field) [StoreCore.setField(table, key, 2, thirdDataBytes)]: 57450 -(src/test/StoreCore.t.sol) | get dynamic field (1 slot, first dynamic field) [loadedData = StoreCore.getField(table, key, 2)]: 5348 -(src/test/StoreCore.t.sol) | set dynamic field (1 slot, second dynamic field) [StoreCore.setField(table, key, 3, fourthDataBytes)]: 35584 -(src/test/StoreCore.t.sol) | get dynamic field (1 slot, second dynamic field) [loadedData = StoreCore.getField(table, key, 3)]: 5363 -(src/test/StoreCore.t.sol) | set static record (1 slot) [StoreCore.setRecord(table, key, data)]: 34213 -(src/test/StoreCore.t.sol) | get static record (1 slot) [bytes memory loadedData = StoreCore.getRecord(table, key, schema)]: 4099 -(src/test/StoreCore.t.sol) | set static record (2 slots) [StoreCore.setRecord(table, key, data)]: 56828 -(src/test/StoreCore.t.sol) | get static record (2 slots) [bytes memory loadedData = StoreCore.getRecord(table, key, schema)]: 4988 -(src/test/StoreSwitch.t.sol) | check if delegatecall [isDelegate = StoreSwitch.isDelegateCall()]: 671 -(src/test/StoreSwitch.t.sol) | check if delegatecall [isDelegate = StoreSwitch.isDelegateCall()]: 627 -(src/test/System.t.sol) | extract msg.sender from calldata [address sender = _msgSender()]: 24 -(src/test/Vector2Table.t.sol) | register Vector2Table schema [Vector2Table.registerSchema()]: 28702 -(src/test/Vector2Table.t.sol) | set Vector2Table record [Vector2Table.set({ key: key, x: 1, y: 2 })]: 35283 -(src/test/Vector2Table.t.sol) | get Vector2Table record [Vector2 memory vector = Vector2Table.get(key)]: 6434 -(src/test/World.t.sol) | call autonomous system via World contract [WorldWithWorldTestSystem(address(world)).WorldTestSystem_move(entity, 1, 2)]: 45582 -(src/test/World.t.sol) | call delegate system via World contract [WorldWithWorldTestSystem(address(world)).WorldTestSystem_move(entity, 1, 2)]: 43447 \ No newline at end of file +(test/Buffer.t.sol) | allocate a buffer [Buffer buf = Buffer_.allocate(32)]: 73 +(test/Buffer.t.sol) | get buffer length [buf.length()]: 87 +(test/Buffer.t.sol) | get buffer pointer [buf.ptr()]: 33 +(test/Buffer.t.sol) | get buffer capacity [buf.capacity()]: 7 +(test/Buffer.t.sol) | append unchecked bytes memory (8) to buffer [buf.appendUnchecked(data1)]: 478 +(test/Buffer.t.sol) | append bytes memory (8) to buffer [buf.append(data2)]: 773 +(test/Buffer.t.sol) | append unchecked bytes8 of bytes32 to buffer [buf.appendUnchecked(data1, 8)]: 351 +(test/Buffer.t.sol) | append bytes8 of bytes32 to buffer [buf.append(data2, 8)]: 645 +(test/Buffer.t.sol) | concat 3 bytes memory (32) using buffer [Buffer buf = Buffer_.concat(data1, data2, data3)]: 2638 +(test/Buffer.t.sol) | concat 3 bytes memory (32) using bytes.concat [bytes memory concat = bytes.concat(data1, data2, data3)]: 641 +(test/Buffer.t.sol) | create a buffer from 8 bytes [Buffer buf = Buffer_.fromBytes(data)]: 40 +(test/Buffer.t.sol) | read bytes32 from buffer [bytes32 value = buf.read32(4)]: 102 +(test/Buffer.t.sol) | read bytes8 with offset 3 from buffer [bytes8 value2 = buf.read8(3)]: 151 +(test/Buffer.t.sol) | read bytes1 with offset 7 from buffer [bytes1 value3 = buf.read1(7)]: 151 +(test/Buffer.t.sol) | set buffer length unchecked [buf._setLengthUnchecked(8)]: 86 +(test/Buffer.t.sol) | set buffer length [buf._setLength(16)]: 192 +(test/Buffer.t.sol) | slice 4 bytes from buffer with offset 4 [bytes memory slice = buf.slice(4, 4)]: 635 +(test/Buffer.t.sol) | convert array pointer to uint256[] [uint256[] memory arr = Cast.toUint256Array(arrayPtr)]: 10 +(test/Buffer.t.sol) | buffer toArray with element length 4 [uint256 arrayPtr = buf.toArray(4)]: 745 +(test/Buffer.t.sol) | convert array pointer to uint32[] [uint32[] memory arr = Cast.toUint32Array(arrayPtr)]: 10 +(test/Buffer.t.sol) | buffer (32 bytes) to bytes memory [bytes memory bufferData = buf.toBytes()]: 90 +(test/Bytes.t.sol) | compare equal bytes [bool equals = Bytes.equals(a, b)]: 202 +(test/Bytes.t.sol) | compare unequal bytes [bool equals = Bytes.equals(a, b)]: 202 +(test/Bytes.t.sol) | create bytes from address [bytes memory output = Bytes.from(input)]: 157 +(test/Bytes.t.sol) | create address from bytes [address output2 = Bytes.toAddress(output)]: 166 +(test/Bytes.t.sol) | create bytes from bytes4 [bytes memory output = Bytes.from(input)]: 151 +(test/Bytes.t.sol) | create bytes4 from bytes [bytes4 output2 = Bytes.toBytes4(output)]: 160 +(test/Bytes.t.sol) | create bytes from uint32 [bytes memory output = Bytes.from(input)]: 149 +(test/Bytes.t.sol) | create uint32 from bytes [uint32 output2 = Bytes.toUint32(output)]: 166 +(test/Bytes.t.sol) | create uint32 array from bytes memory [uint32[] memory output = Bytes.toUint32Array(tight)]: 835 +(test/Bytes.t.sol) | create bytes from uint8 [bytes memory output = Bytes.fromUint8(input)]: 160 +(test/Bytes.t.sol) | create uint8 from bytes [uint8 output2 = Bytes.toUint8(output)]: 105 +(test/Bytes.t.sol) | create bytes from bytes array [bytes memory output = Bytes.from(input)]: 1290 +(test/Bytes.t.sol) | create bytes from uint16 array [bytes memory output = Bytes.from(input)]: 791 +(test/Bytes.t.sol) | create bytes from uint32 array [bytes memory output = Bytes.from(input)]: 695 +(test/Bytes.t.sol) | create bytes from uint8 array [bytes memory output = Bytes.from(input)]: 695 +(test/Bytes.t.sol) | set bytes1 in bytes32 [Bytes.setBytes1(input, 8, 0xff)]: 7 +(test/Bytes.t.sol) | set bytes2 in bytes32 [Bytes.setBytes2(input, 8, 0xffff)]: 7 +(test/Bytes.t.sol) | set bytes4 in bytes32 [Bytes.setBytes4(input, 8, 0xffffffff)]: 7 +(test/Bytes.t.sol) | set length of bytes in place [Bytes.setLengthInPlace(a, 2)]: 16 +(test/Bytes.t.sol) | slice bytes (with copying) with offset 1 and length 3 [bytes memory b = Bytes.slice(a, 1, 3)]: 516 +(test/Bytes.t.sol) | slice bytes3 with offset 1 [bytes3 b = Bytes.slice3(a, 1)]: 77 +(test/Bytes.t.sol) | slice bytes32 with offset 10 [bytes32 output = Bytes.slice32(input, 10)]: 74 +(test/Bytes.t.sol) | tightly pack bytes24 array into bytes array [bytes memory tight = Bytes.from(input)]: 477 +(test/Bytes.t.sol) | create uint32 array from bytes memory [bytes24[] memory output = Bytes.toBytes24Array(tight)]: 614 +(test/Bytes.t.sol) | create bytes32 from bytes memory with offset 0 [bytes32 output = Bytes.toBytes32(input, 0)]: 22 +(test/Bytes.t.sol) | create bytes32 array from bytes memory [bytes32[] memory output = Bytes.toBytes32Array(input)]: 1110 +(test/Bytes.t.sol) | create bytes32 array from bytes memory with uneven length [bytes32[] memory output = Bytes.toBytes32Array(input)]: 1425 +(test/Bytes.t.sol) | create bytes32 from bytes memory with offset 16 [bytes32 output = Bytes.toBytes32(input, 16)]: 22 +(test/Gas.t.sol) | abi encode [bytes memory abiEncoded = abi.encode(mixed)]: 930 +(test/Gas.t.sol) | abi decode [Mixed memory abiDecoded = abi.decode(abiEncoded, (Mixed))]: 1713 +(test/Gas.t.sol) | custom encode [bytes memory customEncoded = customEncode(mixed)]: 1393 +(test/Gas.t.sol) | custom decode [Mixed memory customDecoded = customDecode(customEncoded)]: 2772 +(test/Gas.t.sol) | pass abi encoded bytes to external contract [someContract.doSomethingWithBytes(abiEncoded)]: 6537 +(test/Gas.t.sol) | pass custom encoded bytes to external contract [someContract.doSomethingWithBytes(customEncoded)]: 1342 +(test/MixedTable.t.sol) | store Mixed struct in storage (native solidity) [testMixed = mixed]: 92016 +(test/MixedTable.t.sol) | register MixedTable schema [MixedTable.registerSchema()]: 32525 +(test/MixedTable.t.sol) | set record in MixedTable [MixedTable.set({ key: key, u32: 1, u128: 2, a32: a32, s: s })]: 114716 +(test/MixedTable.t.sol) | get record from MixedTable [Mixed memory mixed = MixedTable.get(key)]: 20494 +(test/PackedCounter.t.sol) | get value at index of PackedCounter [packedCounter.atIndex(3)]: 272 +(test/PackedCounter.t.sol) | set value at index of PackedCounter [packedCounter = packedCounter.setAtIndex(2, 5)]: 884 +(test/PackedCounter.t.sol) | pack uint16 array into PackedCounter [PackedCounter packedCounter = PackedCounterLib.pack(counters)]: 2148 +(test/PackedCounter.t.sol) | get total of PackedCounter [packedCounter.total()]: 33 +(test/RouteTable.t.sol) | register RouteTable schema [RouteTable.registerSchema()]: 30469 +(test/RouteTable.t.sol) | set RouteTable record [RouteTable.set(key, addr, selector, executionMode)]: 35317 +(test/RouteTable.t.sol) | get RouteTable record [Route memory systemEntry = RouteTable.get(key)]: 6548 +(test/Schema.t.sol) | encode schema with 6 entries [SchemaLib.encode]: 6202 +(test/Schema.t.sol) | get schema type at index [SchemaType schemaType1 = schema.atIndex(0)]: 200 +(test/Schema.t.sol) | get number of dynamic fields from schema [uint256 num = schema.numDynamicFields()]: 80 +(test/Schema.t.sol) | get number of static fields from schema [uint256 num = schema.numStaticFields()]: 91 +(test/Schema.t.sol) | get static data length from schema [uint256 length = schema.staticDataLength()]: 39 +(test/Schema.t.sol) | check if schema is empty (non-empty schema) [bool empty = encodedSchema.isEmpty()]: 13 +(test/Schema.t.sol) | check if schema is empty (empty schema) [bool empty = encodedSchema.isEmpty()]: 13 +(test/Schema.t.sol) | validate schema [encodedSchema.validate()]: 22716 +(test/Storage.t.sol) | store 1 storage slot [Storage.store(storagePointer, originalDataFirstSlot)]: 23509 +(test/Storage.t.sol) | store 34 bytes over 3 storage slots (with offset and safeTrail)) [Storage.store(storagePointer, 31, data1)]: 25749 +(test/Storage.t.sol) | load 34 bytes over 3 storage slots (with offset and safeTrail)) [bytes memory data = Storage.load(storagePointer, 31, 34)]: 4131 +(test/StoreCore.t.sol) | access non-existing record [bytes memory data1 = StoreCore.getRecord(table, key)]: 10081 +(test/StoreCore.t.sol) | access static field of non-existing record [bytes memory data2 = StoreCore.getField(table, key, 0)]: 4499 +(test/StoreCore.t.sol) | access dynamic field of non-existing record [bytes memory data3 = StoreCore.getField(table, key, 1)]: 3931 +(test/StoreCore.t.sol) | delete record (complex data, 3 slots) [StoreCore.deleteRecord(table, key)]: 18257 +(test/StoreCore.t.sol) | Check for existence of table (existent) [StoreCore.hasTable(table)]: 989 +(test/StoreCore.t.sol) | check for existence of table (non-existent) [StoreCore.hasTable(table2)]: 2990 +(test/StoreCore.t.sol) | register subscriber [StoreCore.registerHook(table, subscriber)]: 70398 +(test/StoreCore.t.sol) | set record on table with subscriber [StoreCore.setRecord(table, key, data)]: 72319 +(test/StoreCore.t.sol) | set static field on table with subscriber [StoreCore.setField(table, key, 0, data)]: 34395 +(test/StoreCore.t.sol) | delete record on table with subscriber [StoreCore.deleteRecord(table, key)]: 24819 +(test/StoreCore.t.sol) | register subscriber [StoreCore.registerHook(table, subscriber)]: 70398 +(test/StoreCore.t.sol) | set (dynamic) record on table with subscriber [StoreCore.setRecord(table, key, data)]: 175235 +(test/StoreCore.t.sol) | set (dynamic) field on table with subscriber [StoreCore.setField(table, key, 1, arrayDataBytes)]: 37168 +(test/StoreCore.t.sol) | delete (dynamic) record on table with subscriber [StoreCore.deleteRecord(table, key)]: 38680 +(test/StoreCore.t.sol) | StoreCore: register schema [StoreCore.registerSchema(table, schema)]: 26413 +(test/StoreCore.t.sol) | StoreCore: get schema (warm) [Schema loadedSchema = StoreCore.getSchema(table)]: 934 +(test/StoreCore.t.sol) | set complex record with dynamic data (4 slots) [StoreCore.setRecord(table, key, data)]: 110898 +(test/StoreCore.t.sol) | get complex record with dynamic data (4 slots) [bytes memory loadedData = StoreCore.getRecord(table, key)]: 12740 +(test/StoreCore.t.sol) | compare: Set complex record with dynamic data using native solidity [testStruct = _testStruct]: 116815 +(test/StoreCore.t.sol) | compare: Set complex record with dynamic data using abi.encode [testMapping[1234] = abi.encode(_testStruct)]: 267529 +(test/StoreCore.t.sol) | set dynamic length of dynamic index 0 [StoreCoreInternal._setDynamicDataLengthAtIndex(table, key, 0, 10)]: 23716 +(test/StoreCore.t.sol) | set dynamic length of dynamic index 1 [StoreCoreInternal._setDynamicDataLengthAtIndex(table, key, 1, 99)]: 1817 +(test/StoreCore.t.sol) | reduce dynamic length of dynamic index 0 [StoreCoreInternal._setDynamicDataLengthAtIndex(table, key, 0, 5)]: 1818 +(test/StoreCore.t.sol) | set static field (1 slot) [StoreCore.setField(table, key, 0, bytes.concat(firstDataBytes))]: 38177 +(test/StoreCore.t.sol) | get static field (1 slot) [bytes memory loadedData = StoreCore.getField(table, key, 0)]: 4589 +(test/StoreCore.t.sol) | set static field (overlap 2 slot) [StoreCore.setField(table, key, 1, bytes.concat(secondDataBytes))]: 34007 +(test/StoreCore.t.sol) | get static field (overlap 2 slot) [loadedData = StoreCore.getField(table, key, 1)]: 6373 +(test/StoreCore.t.sol) | set dynamic field (1 slot, first dynamic field) [StoreCore.setField(table, key, 2, thirdDataBytes)]: 57450 +(test/StoreCore.t.sol) | get dynamic field (1 slot, first dynamic field) [loadedData = StoreCore.getField(table, key, 2)]: 5348 +(test/StoreCore.t.sol) | set dynamic field (1 slot, second dynamic field) [StoreCore.setField(table, key, 3, fourthDataBytes)]: 35584 +(test/StoreCore.t.sol) | get dynamic field (1 slot, second dynamic field) [loadedData = StoreCore.getField(table, key, 3)]: 5363 +(test/StoreCore.t.sol) | set static record (1 slot) [StoreCore.setRecord(table, key, data)]: 34213 +(test/StoreCore.t.sol) | get static record (1 slot) [bytes memory loadedData = StoreCore.getRecord(table, key, schema)]: 4099 +(test/StoreCore.t.sol) | set static record (2 slots) [StoreCore.setRecord(table, key, data)]: 56828 +(test/StoreCore.t.sol) | get static record (2 slots) [bytes memory loadedData = StoreCore.getRecord(table, key, schema)]: 4988 +(test/StoreSwitch.t.sol) | check if delegatecall [isDelegate = StoreSwitch.isDelegateCall()]: 671 +(test/StoreSwitch.t.sol) | check if delegatecall [isDelegate = StoreSwitch.isDelegateCall()]: 627 +(test/System.t.sol) | extract msg.sender from calldata [address sender = _msgSender()]: 24 +(test/Vector2Table.t.sol) | register Vector2Table schema [Vector2Table.registerSchema()]: 28702 +(test/Vector2Table.t.sol) | set Vector2Table record [Vector2Table.set({ key: key, x: 1, y: 2 })]: 35283 +(test/Vector2Table.t.sol) | get Vector2Table record [Vector2 memory vector = Vector2Table.get(key)]: 6434 +(test/World.t.sol) | call autonomous system via World contract [WorldWithWorldTestSystem(address(world)).WorldTestSystem_move(entity, 1, 2)]: 45582 +(test/World.t.sol) | call delegate system via World contract [WorldWithWorldTestSystem(address(world)).WorldTestSystem_move(entity, 1, 2)]: 43447 \ No newline at end of file From e8f1727b25df665044ab23eafbb5c0a903409d53 Mon Sep 17 00:00:00 2001 From: alvrs Date: Thu, 2 Feb 2023 11:18:31 +0100 Subject: [PATCH 66/82] refactor(store): use named params for Storage and Memory lib calls, reorder params --- packages/store/src/Memory.sol | 8 --- packages/store/src/Storage.sol | 87 ++++++++-------------------- packages/store/src/StoreCore.sol | 94 +++++++++++++++++-------------- packages/store/test/Storage.t.sol | 24 +++++--- 4 files changed, 90 insertions(+), 123 deletions(-) diff --git a/packages/store/src/Memory.sol b/packages/store/src/Memory.sol index fe8d9fb21b..f8a4b8dcc8 100644 --- a/packages/store/src/Memory.sol +++ b/packages/store/src/Memory.sol @@ -6,20 +6,12 @@ import { Bytes } from "./Bytes.sol"; library Memory { function load(uint256 memoryPointer) internal pure returns (bytes32 data) { - return load(bytes32(memoryPointer)); - } - - function load(bytes32 memoryPointer) internal pure returns (bytes32 data) { assembly { data := mload(memoryPointer) } } function load(uint256 memoryPointer, uint256 offset) internal pure returns (bytes32 data) { - return load(bytes32(memoryPointer), offset); - } - - function load(bytes32 memoryPointer, uint256 offset) internal pure returns (bytes32 data) { assembly { data := mload(add(memoryPointer, offset)) } diff --git a/packages/store/src/Storage.sol b/packages/store/src/Storage.sol index f1865eecdf..146e7772f2 100644 --- a/packages/store/src/Storage.sol +++ b/packages/store/src/Storage.sol @@ -8,30 +8,14 @@ import { Memory } from "./Memory.sol"; import "./Buffer.sol"; library Storage { - function store(bytes32 storagePointer, bytes memory data) internal { - store(uint256(storagePointer), 0, data); - } - function store(uint256 storagePointer, bytes memory data) internal { store(storagePointer, 0, data); } - function store(bytes32 storagePointer, bytes32 data) internal { - _storeWord(uint256(storagePointer), data); - } - function store(uint256 storagePointer, bytes32 data) internal { _storeWord(storagePointer, data); } - function store( - bytes32 storagePointer, - uint256 offset, - bytes memory data - ) internal { - store(uint256(storagePointer), offset, data); - } - function store( uint256 storagePointer, uint256 offset, @@ -44,18 +28,9 @@ library Storage { store(storagePointer, offset, memoryPointer, data.length); } - function store( - bytes32 storagePointer, - uint256 offset, - uint256 memoryPointer, - uint256 length - ) internal { - store(uint256(storagePointer), offset, memoryPointer, length); - } - /** - * @dev store raw bytes to storage at the given storagePointer and offset (keeping the rest of the word intact) - * TODO: this implementation is optimized for readability, but not very gas efficient. We should optimize this using assembly once we've settled on a spec. + * @notice Stores raw bytes to storage at the given storagePointer and offset (keeping the rest of the word intact) + * @dev This implementation is optimized for readability, but not very gas efficient. We should optimize this using assembly once we've settled on a spec. */ function store( uint256 storagePointer, @@ -76,72 +51,56 @@ library Storage { uint256 _lengthTostore = length + offset > 32 ? 32 - offset : length; // // the number of bytes to store _storePartialWord( storagePointer, // the word to update - offset, // the offset in bytes to start writing _lengthTostore, - Memory.load(memoryPointer) // Pass the first 32 bytes of the data + offset, // the offset in bytes to start writing + Memory.load({ memoryPointer: memoryPointer }) // Pass the first 32 bytes of the data ); bytesWritten += _lengthTostore; // If this is the last word, and there is a partial word, apply a mask to the end } else if (i == numWords - 1 && (length + offset) % 32 > 0) { _storePartialWord( storagePointer + i, // the word to update - 0, // the offset in bytes to start writing (length + offset) % 32, // the number of bytes to store - Memory.load(memoryPointer, bytesWritten) // the data to store + 0, // the offset in bytes to start writing + Memory.load({ memoryPointer: memoryPointer, offset: bytesWritten }) // the data to store ); // Else, just store the word } else { - _storeWord(storagePointer + i, Memory.load(memoryPointer, bytesWritten)); + _storeWord(storagePointer + i, Memory.load({ memoryPointer: memoryPointer, offset: bytesWritten })); bytesWritten += 32; } } } - function load(bytes32 storagePointer) internal view returns (bytes32) { - return _loadWord(uint256(storagePointer)); - } - function load(uint256 storagePointer) internal view returns (bytes32) { return _loadWord(storagePointer); } - function load(bytes32 storagePointer, uint256 length) internal view returns (bytes memory) { - return load(uint256(storagePointer), 0, length); - } - function load(uint256 storagePointer, uint256 length) internal view returns (bytes memory) { - return load(storagePointer, 0, length); - } - - function load( - bytes32 storagePointer, - uint256 offset, - uint256 length - ) internal view returns (bytes memory) { - return load(uint256(storagePointer), offset, length); + return load(storagePointer, length, 0); } /** - * @dev load raw bytes from storage at the given storagePointer, offset, and length + * @notice Load raw bytes from storage at the given storagePointer, offset, and length */ function load( uint256 storagePointer, - uint256 offset, - uint256 length + uint256 length, + uint256 offset ) internal view returns (bytes memory) { Buffer buffer = Buffer_.allocate(uint128(length)); - load(storagePointer, offset, length, buffer); + load(storagePointer, length, offset, buffer); return buffer.toBytes(); } /** - * @dev Append raw bytes from storage at the given storagePointer, offset, and length to the given buffer + * @notice Append raw bytes from storage at the given storagePointer, offset, and length to the given buffer */ function load( uint256 storagePointer, - uint256 offset, uint256 length, + uint256 offset, Buffer buffer ) internal view { // Support offsets that are greater than 32 bytes by incrementing the storagePointer and decrementing the offset @@ -158,8 +117,8 @@ library Storage { buffer.appendUnchecked( _loadPartialWord( storagePointer, // the slot to start loading from - offset, // the offset in bytes to start loading from - _lengthToload + _lengthToload, + offset // the offset in bytes to start loading from ), uint128(_lengthToload) ); @@ -170,8 +129,8 @@ library Storage { buffer.appendUnchecked( _loadPartialWord( storagePointer + i, // the word to load from - 0, // the offset in bytes to start loading from - _lengthToload + _lengthToload, + 0 // the offset in bytes to start loading from ), uint128(_lengthToload) ); @@ -184,7 +143,7 @@ library Storage { } /** - * @dev Load a full word from storage into memory + * @notice Load a full word from storage into memory */ function _loadWord(uint256 storagePointer) internal view returns (bytes32 data) { assembly { @@ -193,12 +152,12 @@ library Storage { } /** - * @dev Load a partial word from storage into memory + * @notice Load a partial word from storage into memory */ function _loadPartialWord( uint256 storagePointer, - uint256 offset, - uint256 length + uint256 length, + uint256 offset ) internal view returns (bytes32) { // Load current value from storage bytes32 storageValue; @@ -218,8 +177,8 @@ library Storage { function _storePartialWord( uint256 storagePointer, - uint256 offset, // in bytes uint256 length, // in bytes + uint256 offset, // in bytes bytes32 data ) internal { bytes32 current; diff --git a/packages/store/src/StoreCore.sol b/packages/store/src/StoreCore.sol index 50a3a28b4a..963b02f096 100644 --- a/packages/store/src/StoreCore.sol +++ b/packages/store/src/StoreCore.sol @@ -12,11 +12,6 @@ import { Buffer, Buffer_ } from "./Buffer.sol"; import { HooksTable, tableId as HooksTableId } from "./tables/HooksTable.sol"; import { IStoreHook } from "./IStore.sol"; -// TODO -// - Turn all storage pointer to uint256 for consistency (uint256 is better than bytes32 because it's easier to do arithmetic on) -// - Change Storage library functions to make it clearer which argument is offset and which is length -// - Streamline naming in Storage and Memory libraries (probably just use load and store instead of read and write?) - 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 MudStoreSetRecord(bytes32 table, bytes32[] key, bytes data); @@ -127,25 +122,35 @@ library StoreCore { } // Store the static data at the static data location - bytes32 staticDataLocation = StoreCoreInternal._getStaticDataLocation(table, key); + uint256 staticDataLocation = StoreCoreInternal._getStaticDataLocation(table, key); uint256 memoryPointer = Memory.dataPointer(data); - Storage.store(staticDataLocation, 0, memoryPointer, staticLength); + Storage.store({ + storagePointer: staticDataLocation, + offset: 0, + memoryPointer: memoryPointer, + length: staticLength + }); memoryPointer += staticLength + 32; // move the memory pointer to the start of the dynamic data (skip the encoded dynamic length) // If there is no dynamic data, we're done if (schema.numDynamicFields() == 0) return; // Store the dynamic data length at the dynamic data length location - bytes32 dynamicDataLengthLocation = StoreCoreInternal._getDynamicDataLengthLocation(table, key); - Storage.store(dynamicDataLengthLocation, dynamicLength.unwrap()); + uint256 dynamicDataLengthLocation = StoreCoreInternal._getDynamicDataLengthLocation(table, key); + Storage.store({ storagePointer: dynamicDataLengthLocation, data: dynamicLength.unwrap() }); // For every dynamic element, slice off the dynamic data and store it at the dynamic location - bytes32 dynamicDataLocation; + uint256 dynamicDataLocation; uint256 dynamicDataLength; for (uint8 i; i < schema.numDynamicFields(); ) { dynamicDataLocation = StoreCoreInternal._getDynamicDataLocation(table, key, i); dynamicDataLength = dynamicLength.atIndex(i); - Storage.store(dynamicDataLocation, 0, memoryPointer, dynamicDataLength); + Storage.store({ + storagePointer: dynamicDataLocation, + offset: 0, + memoryPointer: memoryPointer, + length: dynamicDataLength + }); memoryPointer += dynamicDataLength; // move the memory pointer to the start of the next dynamic data unchecked { i++; @@ -193,26 +198,26 @@ library StoreCore { } // Delete static data - bytes32 staticDataLocation = StoreCoreInternal._getStaticDataLocation(table, key); - Storage.store(staticDataLocation, 0, new bytes(schema.staticDataLength())); + uint256 staticDataLocation = StoreCoreInternal._getStaticDataLocation(table, key); + Storage.store({ storagePointer: staticDataLocation, offset: 0, data: new bytes(schema.staticDataLength()) }); // If there are no dynamic fields, we're done if (schema.numDynamicFields() == 0) return; // Delete dynamic data - bytes32 dynamicDataLocation; + uint256 dynamicDataLocation; PackedCounter encodedLengths = StoreCoreInternal._loadEncodedDynamicDataLength(table, key); for (uint8 i; i < schema.numDynamicFields(); ) { dynamicDataLocation = StoreCoreInternal._getDynamicDataLocation(table, key, i); - Storage.store(dynamicDataLocation, 0, new bytes(encodedLengths.atIndex(i))); + Storage.store({ storagePointer: dynamicDataLocation, offset: 0, data: new bytes(encodedLengths.atIndex(i)) }); unchecked { i++; } } // Delete dynamic data length - bytes32 dynamicDataLengthLocation = StoreCoreInternal._getDynamicDataLengthLocation(table, key); - Storage.store(dynamicDataLengthLocation, bytes32(0)); + uint256 dynamicDataLengthLocation = StoreCoreInternal._getDynamicDataLengthLocation(table, key); + Storage.store({ storagePointer: dynamicDataLengthLocation, data: bytes32(0) }); // Emit event to notify indexers emit MudStoreDeleteRecord(table, key); @@ -269,7 +274,12 @@ library StoreCore { // Append dynamic data to the buffer for (uint8 i; i < numDynamicFields; i++) { uint256 dynamicDataLocation = uint256(StoreCoreInternal._getDynamicDataLocation(table, key, i)); - Storage.load(dynamicDataLocation, 0, dynamicDataLength.atIndex(i), buffer); + Storage.load({ + storagePointer: dynamicDataLocation, + length: dynamicDataLength.atIndex(i), + offset: 0, + buffer: buffer + }); } // Return the buffer as bytes @@ -320,8 +330,8 @@ library StoreCoreInternal { function _getSchema(bytes32 table) internal view returns (Schema) { bytes32[] memory key = new bytes32[](1); key[0] = table; - bytes32 location = StoreCoreInternal._getStaticDataLocation(SCHEMA_TABLE, key); - return Schema.wrap(Storage.load(location)); + uint256 location = StoreCoreInternal._getStaticDataLocation(SCHEMA_TABLE, key); + return Schema.wrap(Storage.load({ storagePointer: location })); } /** @@ -330,8 +340,8 @@ library StoreCoreInternal { function _registerSchemaUnchecked(bytes32 table, Schema schema) internal { bytes32[] memory key = new bytes32[](1); key[0] = table; - bytes32 location = _getStaticDataLocation(SCHEMA_TABLE, key); - Storage.store(location, schema.unwrap()); + uint256 location = _getStaticDataLocation(SCHEMA_TABLE, key); + Storage.store({ storagePointer: location, data: schema.unwrap() }); } /************************************************************************ @@ -353,9 +363,9 @@ library StoreCoreInternal { revert StoreCore_InvalidDataLength(getStaticByteLength(schemaType), data.length); // Store the provided value in storage - bytes32 location = _getStaticDataLocation(table, key); + uint256 location = _getStaticDataLocation(table, key); uint256 offset = _getStaticDataOffset(schema, schemaIndex); - Storage.store(location, offset, data); + Storage.store({ storagePointer: location, offset: offset, data: data }); } function _setDynamicField( @@ -371,8 +381,8 @@ library StoreCoreInternal { _setDynamicDataLengthAtIndex(table, key, dynamicSchemaIndex, data.length); // Store the provided value in storage - bytes32 dynamicDataLocation = _getDynamicDataLocation(table, key, dynamicSchemaIndex); - Storage.store(dynamicDataLocation, data); + uint256 dynamicDataLocation = _getDynamicDataLocation(table, key, dynamicSchemaIndex); + Storage.store({ storagePointer: dynamicDataLocation, data: data }); } /** @@ -400,8 +410,8 @@ library StoreCoreInternal { if (schema.staticDataLength() == 0) return new bytes(0); // Load the data from storage - bytes32 location = _getStaticDataLocation(table, key); - return Storage.load(location, schema.staticDataLength()); + uint256 location = _getStaticDataLocation(table, key); + return Storage.load({ storagePointer: location, length: schema.staticDataLength() }); } /** @@ -421,7 +431,7 @@ library StoreCoreInternal { // Load the data from storage - return Storage.load(location, offset, dataLength); + return Storage.load({ storagePointer: location, length: dataLength, offset: offset }); } /** @@ -435,10 +445,10 @@ library StoreCoreInternal { ) internal view returns (bytes memory) { // Get the length and storage location of the dynamic field uint8 dynamicSchemaIndex = schemaIndex - schema.numStaticFields(); - bytes32 location = _getDynamicDataLocation(table, key, dynamicSchemaIndex); + uint256 location = _getDynamicDataLocation(table, key, dynamicSchemaIndex); uint256 dataLength = _loadEncodedDynamicDataLength(table, key).atIndex(dynamicSchemaIndex); - return Storage.load(location, dataLength); + return Storage.load({ storagePointer: location, length: dataLength }); } /************************************************************************ @@ -454,8 +464,8 @@ library StoreCoreInternal { /** * Compute the storage location based on table id and index tuple */ - function _getStaticDataLocation(bytes32 table, bytes32[] memory key) internal pure returns (bytes32) { - return keccak256(abi.encode(SLOT, table, key)); + function _getStaticDataLocation(bytes32 table, bytes32[] memory key) internal pure returns (uint256) { + return uint256(keccak256(abi.encode(SLOT, table, key))); } /** @@ -480,15 +490,15 @@ library StoreCoreInternal { bytes32 table, bytes32[] memory key, uint8 schemaIndex - ) internal pure returns (bytes32) { - return keccak256(abi.encode(SLOT, table, key, schemaIndex)); + ) internal pure returns (uint256) { + return uint256(keccak256(abi.encode(SLOT, table, key, schemaIndex))); } /** * Compute the storage location for the length of the dynamic data */ - function _getDynamicDataLengthLocation(bytes32 table, bytes32[] memory key) internal pure returns (bytes32) { - return keccak256(abi.encode(SLOT, table, key, "length")); + function _getDynamicDataLengthLocation(bytes32 table, bytes32[] memory key) internal pure returns (uint256) { + return uint256(keccak256(abi.encode(SLOT, table, key, "length"))); } /** @@ -496,8 +506,8 @@ library StoreCoreInternal { */ function _loadEncodedDynamicDataLength(bytes32 table, bytes32[] memory key) internal view returns (PackedCounter) { // Load dynamic data length from storage - bytes32 dynamicSchemaLengthSlot = _getDynamicDataLengthLocation(table, key); - return PackedCounter.wrap(Storage.load(dynamicSchemaLengthSlot)); + uint256 dynamicSchemaLengthSlot = _getDynamicDataLengthLocation(table, key); + return PackedCounter.wrap(Storage.load({ storagePointer: dynamicSchemaLengthSlot })); } /** @@ -510,14 +520,14 @@ library StoreCoreInternal { uint256 newLengthAtIndex ) internal { // Load dynamic data length from storage - bytes32 dynamicSchemaLengthSlot = _getDynamicDataLengthLocation(table, key); - PackedCounter encodedLengths = PackedCounter.wrap(Storage.load(dynamicSchemaLengthSlot)); + uint256 dynamicSchemaLengthSlot = _getDynamicDataLengthLocation(table, key); + PackedCounter encodedLengths = PackedCounter.wrap(Storage.load({ storagePointer: dynamicSchemaLengthSlot })); // Update the encoded lengths encodedLengths = encodedLengths.setAtIndex(dynamicSchemaIndex, newLengthAtIndex); // Set the new lengths - Storage.store(dynamicSchemaLengthSlot, encodedLengths.unwrap()); + Storage.store({ storagePointer: dynamicSchemaLengthSlot, data: encodedLengths.unwrap() }); } } diff --git a/packages/store/test/Storage.t.sol b/packages/store/test/Storage.t.sol index 19d992b6a7..435f28a52a 100644 --- a/packages/store/test/Storage.t.sol +++ b/packages/store/test/Storage.t.sol @@ -28,34 +28,37 @@ contract StorageTest is Test { // First store some data to storage at the target slot and two slots after the target slot // !gasreport store 1 storage slot - Storage.store(storagePointer, originalDataFirstSlot); + Storage.store({ storagePointer: storagePointer, data: originalDataFirstSlot }); - Storage.store(storagePointerTwoSlotsAfter, originalDataLastSlot); + Storage.store({ storagePointer: storagePointerTwoSlotsAfter, data: originalDataLastSlot }); // Then set the target slot, partially overwriting the first and third slot, but using safeTrail and offset // !gasreport store 34 bytes over 3 storage slots (with offset and safeTrail)) - Storage.store(storagePointer, 31, data1); + Storage.store({ storagePointer: storagePointer, offset: 31, data: data1 }); // Assert the first slot has the correct value - assertEq(Storage.load(storagePointer), bytes32(0x4200000000000000000000000000000000000000000000000000000000006901)); + assertEq( + Storage.load({ storagePointer: storagePointer }), + bytes32(0x4200000000000000000000000000000000000000000000000000000000006901) + ); // Assert the second slot has the correct value assertEq( - Storage.load(storagePointer + 1), + Storage.load({ storagePointer: storagePointer + 1 }), bytes32(0x0200000000000000000000000000000000000000000000000000000000000003) ); // Assert that the trailing slot has the correct value assertEq( - Storage.load(storagePointerTwoSlotsAfter), + Storage.load({ storagePointer: storagePointerTwoSlotsAfter }), bytes32(0x0442000000000000000000000000000000000000000000000000000000000069) ); // Assert we can load the correct partial value from storage // !gasreport load 34 bytes over 3 storage slots (with offset and safeTrail)) - bytes memory data = Storage.load(storagePointer, 31, 34); + bytes memory data = Storage.load({ storagePointer: storagePointer, length: 34, offset: 31 }); assertEq(Bytes.slice1(data, 0), bytes1(0x01)); assertEq(Bytes.slice32(data, 1), bytes32(0x0200000000000000000000000000000000000000000000000000000000000003)); @@ -68,7 +71,10 @@ contract StorageTest is Test { bytes32 storagePointer, uint8 offset ) public { - Storage.store(storagePointer, offset, data); - assertEq(keccak256(Storage.load(storagePointer, offset, data.length)), keccak256(data)); + Storage.store({ storagePointer: uint256(storagePointer), offset: offset, data: data }); + assertEq( + keccak256(Storage.load({ storagePointer: uint256(storagePointer), length: data.length, offset: offset })), + keccak256(data) + ); } } From eb04849bf4f960c9d4479fc70d818cda3d30c48a Mon Sep 17 00:00:00 2001 From: alvrs Date: Thu, 2 Feb 2023 10:25:30 +0000 Subject: [PATCH 67/82] test: update gas report --- packages/store/gas-report.txt | 94 +++++++++++++++++------------------ 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/packages/store/gas-report.txt b/packages/store/gas-report.txt index 14314c6445..31d5871c3f 100644 --- a/packages/store/gas-report.txt +++ b/packages/store/gas-report.txt @@ -54,16 +54,16 @@ (test/Gas.t.sol) | pass abi encoded bytes to external contract [someContract.doSomethingWithBytes(abiEncoded)]: 6537 (test/Gas.t.sol) | pass custom encoded bytes to external contract [someContract.doSomethingWithBytes(customEncoded)]: 1342 (test/MixedTable.t.sol) | store Mixed struct in storage (native solidity) [testMixed = mixed]: 92016 -(test/MixedTable.t.sol) | register MixedTable schema [MixedTable.registerSchema()]: 32525 -(test/MixedTable.t.sol) | set record in MixedTable [MixedTable.set({ key: key, u32: 1, u128: 2, a32: a32, s: s })]: 114716 -(test/MixedTable.t.sol) | get record from MixedTable [Mixed memory mixed = MixedTable.get(key)]: 20494 +(test/MixedTable.t.sol) | register MixedTable schema [MixedTable.registerSchema()]: 32531 +(test/MixedTable.t.sol) | set record in MixedTable [MixedTable.set({ key: key, u32: 1, u128: 2, a32: a32, s: s })]: 114470 +(test/MixedTable.t.sol) | get record from MixedTable [Mixed memory mixed = MixedTable.get(key)]: 20506 (test/PackedCounter.t.sol) | get value at index of PackedCounter [packedCounter.atIndex(3)]: 272 (test/PackedCounter.t.sol) | set value at index of PackedCounter [packedCounter = packedCounter.setAtIndex(2, 5)]: 884 (test/PackedCounter.t.sol) | pack uint16 array into PackedCounter [PackedCounter packedCounter = PackedCounterLib.pack(counters)]: 2148 (test/PackedCounter.t.sol) | get total of PackedCounter [packedCounter.total()]: 33 -(test/RouteTable.t.sol) | register RouteTable schema [RouteTable.registerSchema()]: 30469 -(test/RouteTable.t.sol) | set RouteTable record [RouteTable.set(key, addr, selector, executionMode)]: 35317 -(test/RouteTable.t.sol) | get RouteTable record [Route memory systemEntry = RouteTable.get(key)]: 6548 +(test/RouteTable.t.sol) | register RouteTable schema [RouteTable.registerSchema()]: 30475 +(test/RouteTable.t.sol) | set RouteTable record [RouteTable.set(key, addr, selector, executionMode)]: 35240 +(test/RouteTable.t.sol) | get RouteTable record [Route memory systemEntry = RouteTable.get(key)]: 6554 (test/Schema.t.sol) | encode schema with 6 entries [SchemaLib.encode]: 6202 (test/Schema.t.sol) | get schema type at index [SchemaType schemaType1 = schema.atIndex(0)]: 200 (test/Schema.t.sol) | get number of dynamic fields from schema [uint256 num = schema.numDynamicFields()]: 80 @@ -72,49 +72,49 @@ (test/Schema.t.sol) | check if schema is empty (non-empty schema) [bool empty = encodedSchema.isEmpty()]: 13 (test/Schema.t.sol) | check if schema is empty (empty schema) [bool empty = encodedSchema.isEmpty()]: 13 (test/Schema.t.sol) | validate schema [encodedSchema.validate()]: 22716 -(test/Storage.t.sol) | store 1 storage slot [Storage.store(storagePointer, originalDataFirstSlot)]: 23509 -(test/Storage.t.sol) | store 34 bytes over 3 storage slots (with offset and safeTrail)) [Storage.store(storagePointer, 31, data1)]: 25749 -(test/Storage.t.sol) | load 34 bytes over 3 storage slots (with offset and safeTrail)) [bytes memory data = Storage.load(storagePointer, 31, 34)]: 4131 -(test/StoreCore.t.sol) | access non-existing record [bytes memory data1 = StoreCore.getRecord(table, key)]: 10081 -(test/StoreCore.t.sol) | access static field of non-existing record [bytes memory data2 = StoreCore.getField(table, key, 0)]: 4499 -(test/StoreCore.t.sol) | access dynamic field of non-existing record [bytes memory data3 = StoreCore.getField(table, key, 1)]: 3931 -(test/StoreCore.t.sol) | delete record (complex data, 3 slots) [StoreCore.deleteRecord(table, key)]: 18257 -(test/StoreCore.t.sol) | Check for existence of table (existent) [StoreCore.hasTable(table)]: 989 -(test/StoreCore.t.sol) | check for existence of table (non-existent) [StoreCore.hasTable(table2)]: 2990 -(test/StoreCore.t.sol) | register subscriber [StoreCore.registerHook(table, subscriber)]: 70398 -(test/StoreCore.t.sol) | set record on table with subscriber [StoreCore.setRecord(table, key, data)]: 72319 -(test/StoreCore.t.sol) | set static field on table with subscriber [StoreCore.setField(table, key, 0, data)]: 34395 -(test/StoreCore.t.sol) | delete record on table with subscriber [StoreCore.deleteRecord(table, key)]: 24819 -(test/StoreCore.t.sol) | register subscriber [StoreCore.registerHook(table, subscriber)]: 70398 -(test/StoreCore.t.sol) | set (dynamic) record on table with subscriber [StoreCore.setRecord(table, key, data)]: 175235 -(test/StoreCore.t.sol) | set (dynamic) field on table with subscriber [StoreCore.setField(table, key, 1, arrayDataBytes)]: 37168 -(test/StoreCore.t.sol) | delete (dynamic) record on table with subscriber [StoreCore.deleteRecord(table, key)]: 38680 -(test/StoreCore.t.sol) | StoreCore: register schema [StoreCore.registerSchema(table, schema)]: 26413 -(test/StoreCore.t.sol) | StoreCore: get schema (warm) [Schema loadedSchema = StoreCore.getSchema(table)]: 934 -(test/StoreCore.t.sol) | set complex record with dynamic data (4 slots) [StoreCore.setRecord(table, key, data)]: 110898 -(test/StoreCore.t.sol) | get complex record with dynamic data (4 slots) [bytes memory loadedData = StoreCore.getRecord(table, key)]: 12740 +(test/Storage.t.sol) | store 1 storage slot [Storage.store({ storagePointer: storagePointer, data: originalDataFirstSlot })]: 23449 +(test/Storage.t.sol) | store 34 bytes over 3 storage slots (with offset and safeTrail)) [Storage.store({ storagePointer: storagePointer, offset: 31, data: data1 })]: 25604 +(test/Storage.t.sol) | load 34 bytes over 3 storage slots (with offset and safeTrail)) [bytes memory data = Storage.load({ storagePointer: storagePointer, length: 34, offset: 31 })]: 4131 +(test/StoreCore.t.sol) | access non-existing record [bytes memory data1 = StoreCore.getRecord(table, key)]: 10090 +(test/StoreCore.t.sol) | access static field of non-existing record [bytes memory data2 = StoreCore.getField(table, key, 0)]: 4505 +(test/StoreCore.t.sol) | access dynamic field of non-existing record [bytes memory data3 = StoreCore.getField(table, key, 1)]: 3940 +(test/StoreCore.t.sol) | delete record (complex data, 3 slots) [StoreCore.deleteRecord(table, key)]: 18029 +(test/StoreCore.t.sol) | Check for existence of table (existent) [StoreCore.hasTable(table)]: 992 +(test/StoreCore.t.sol) | check for existence of table (non-existent) [StoreCore.hasTable(table2)]: 2993 +(test/StoreCore.t.sol) | register subscriber [StoreCore.registerHook(table, subscriber)]: 70377 +(test/StoreCore.t.sol) | set record on table with subscriber [StoreCore.setRecord(table, key, data)]: 72168 +(test/StoreCore.t.sol) | set static field on table with subscriber [StoreCore.setField(table, key, 0, data)]: 34254 +(test/StoreCore.t.sol) | delete record on table with subscriber [StoreCore.deleteRecord(table, key)]: 24678 +(test/StoreCore.t.sol) | register subscriber [StoreCore.registerHook(table, subscriber)]: 70377 +(test/StoreCore.t.sol) | set (dynamic) record on table with subscriber [StoreCore.setRecord(table, key, data)]: 174918 +(test/StoreCore.t.sol) | set (dynamic) field on table with subscriber [StoreCore.setField(table, key, 1, arrayDataBytes)]: 37111 +(test/StoreCore.t.sol) | delete (dynamic) record on table with subscriber [StoreCore.deleteRecord(table, key)]: 38393 +(test/StoreCore.t.sol) | StoreCore: register schema [StoreCore.registerSchema(table, schema)]: 26419 +(test/StoreCore.t.sol) | StoreCore: get schema (warm) [Schema loadedSchema = StoreCore.getSchema(table)]: 937 +(test/StoreCore.t.sol) | set complex record with dynamic data (4 slots) [StoreCore.setRecord(table, key, data)]: 110652 +(test/StoreCore.t.sol) | get complex record with dynamic data (4 slots) [bytes memory loadedData = StoreCore.getRecord(table, key)]: 12755 (test/StoreCore.t.sol) | compare: Set complex record with dynamic data using native solidity [testStruct = _testStruct]: 116815 (test/StoreCore.t.sol) | compare: Set complex record with dynamic data using abi.encode [testMapping[1234] = abi.encode(_testStruct)]: 267529 -(test/StoreCore.t.sol) | set dynamic length of dynamic index 0 [StoreCoreInternal._setDynamicDataLengthAtIndex(table, key, 0, 10)]: 23716 -(test/StoreCore.t.sol) | set dynamic length of dynamic index 1 [StoreCoreInternal._setDynamicDataLengthAtIndex(table, key, 1, 99)]: 1817 -(test/StoreCore.t.sol) | reduce dynamic length of dynamic index 0 [StoreCoreInternal._setDynamicDataLengthAtIndex(table, key, 0, 5)]: 1818 -(test/StoreCore.t.sol) | set static field (1 slot) [StoreCore.setField(table, key, 0, bytes.concat(firstDataBytes))]: 38177 -(test/StoreCore.t.sol) | get static field (1 slot) [bytes memory loadedData = StoreCore.getField(table, key, 0)]: 4589 -(test/StoreCore.t.sol) | set static field (overlap 2 slot) [StoreCore.setField(table, key, 1, bytes.concat(secondDataBytes))]: 34007 -(test/StoreCore.t.sol) | get static field (overlap 2 slot) [loadedData = StoreCore.getField(table, key, 1)]: 6373 -(test/StoreCore.t.sol) | set dynamic field (1 slot, first dynamic field) [StoreCore.setField(table, key, 2, thirdDataBytes)]: 57450 -(test/StoreCore.t.sol) | get dynamic field (1 slot, first dynamic field) [loadedData = StoreCore.getField(table, key, 2)]: 5348 -(test/StoreCore.t.sol) | set dynamic field (1 slot, second dynamic field) [StoreCore.setField(table, key, 3, fourthDataBytes)]: 35584 -(test/StoreCore.t.sol) | get dynamic field (1 slot, second dynamic field) [loadedData = StoreCore.getField(table, key, 3)]: 5363 -(test/StoreCore.t.sol) | set static record (1 slot) [StoreCore.setRecord(table, key, data)]: 34213 -(test/StoreCore.t.sol) | get static record (1 slot) [bytes memory loadedData = StoreCore.getRecord(table, key, schema)]: 4099 -(test/StoreCore.t.sol) | set static record (2 slots) [StoreCore.setRecord(table, key, data)]: 56828 -(test/StoreCore.t.sol) | get static record (2 slots) [bytes memory loadedData = StoreCore.getRecord(table, key, schema)]: 4988 +(test/StoreCore.t.sol) | set dynamic length of dynamic index 0 [StoreCoreInternal._setDynamicDataLengthAtIndex(table, key, 0, 10)]: 23719 +(test/StoreCore.t.sol) | set dynamic length of dynamic index 1 [StoreCoreInternal._setDynamicDataLengthAtIndex(table, key, 1, 99)]: 1820 +(test/StoreCore.t.sol) | reduce dynamic length of dynamic index 0 [StoreCoreInternal._setDynamicDataLengthAtIndex(table, key, 0, 5)]: 1821 +(test/StoreCore.t.sol) | set static field (1 slot) [StoreCore.setField(table, key, 0, bytes.concat(firstDataBytes))]: 38105 +(test/StoreCore.t.sol) | get static field (1 slot) [bytes memory loadedData = StoreCore.getField(table, key, 0)]: 4595 +(test/StoreCore.t.sol) | set static field (overlap 2 slot) [StoreCore.setField(table, key, 1, bytes.concat(secondDataBytes))]: 33895 +(test/StoreCore.t.sol) | get static field (overlap 2 slot) [loadedData = StoreCore.getField(table, key, 1)]: 6379 +(test/StoreCore.t.sol) | set dynamic field (1 slot, first dynamic field) [StoreCore.setField(table, key, 2, thirdDataBytes)]: 57420 +(test/StoreCore.t.sol) | get dynamic field (1 slot, first dynamic field) [loadedData = StoreCore.getField(table, key, 2)]: 5357 +(test/StoreCore.t.sol) | set dynamic field (1 slot, second dynamic field) [StoreCore.setField(table, key, 3, fourthDataBytes)]: 35554 +(test/StoreCore.t.sol) | get dynamic field (1 slot, second dynamic field) [loadedData = StoreCore.getField(table, key, 3)]: 5372 +(test/StoreCore.t.sol) | set static record (1 slot) [StoreCore.setRecord(table, key, data)]: 34136 +(test/StoreCore.t.sol) | get static record (1 slot) [bytes memory loadedData = StoreCore.getRecord(table, key, schema)]: 4102 +(test/StoreCore.t.sol) | set static record (2 slots) [StoreCore.setRecord(table, key, data)]: 56691 +(test/StoreCore.t.sol) | get static record (2 slots) [bytes memory loadedData = StoreCore.getRecord(table, key, schema)]: 4991 (test/StoreSwitch.t.sol) | check if delegatecall [isDelegate = StoreSwitch.isDelegateCall()]: 671 (test/StoreSwitch.t.sol) | check if delegatecall [isDelegate = StoreSwitch.isDelegateCall()]: 627 (test/System.t.sol) | extract msg.sender from calldata [address sender = _msgSender()]: 24 -(test/Vector2Table.t.sol) | register Vector2Table schema [Vector2Table.registerSchema()]: 28702 -(test/Vector2Table.t.sol) | set Vector2Table record [Vector2Table.set({ key: key, x: 1, y: 2 })]: 35283 -(test/Vector2Table.t.sol) | get Vector2Table record [Vector2 memory vector = Vector2Table.get(key)]: 6434 -(test/World.t.sol) | call autonomous system via World contract [WorldWithWorldTestSystem(address(world)).WorldTestSystem_move(entity, 1, 2)]: 45582 -(test/World.t.sol) | call delegate system via World contract [WorldWithWorldTestSystem(address(world)).WorldTestSystem_move(entity, 1, 2)]: 43447 \ No newline at end of file +(test/Vector2Table.t.sol) | register Vector2Table schema [Vector2Table.registerSchema()]: 28708 +(test/Vector2Table.t.sol) | set Vector2Table record [Vector2Table.set({ key: key, x: 1, y: 2 })]: 35206 +(test/Vector2Table.t.sol) | get Vector2Table record [Vector2 memory vector = Vector2Table.get(key)]: 6440 +(test/World.t.sol) | call autonomous system via World contract [WorldWithWorldTestSystem(address(world)).WorldTestSystem_move(entity, 1, 2)]: 45511 +(test/World.t.sol) | call delegate system via World contract [WorldWithWorldTestSystem(address(world)).WorldTestSystem_move(entity, 1, 2)]: 43376 \ No newline at end of file From 7e25d96577ce03e9fd9e277bdb1d861f8629f3b7 Mon Sep 17 00:00:00 2001 From: alvrs Date: Thu, 2 Feb 2023 11:56:15 +0100 Subject: [PATCH 68/82] refactor(store): use abi.encodePacked for fixed length types instead of bytes.concat --- packages/store/src/Bytes.sol | 8 ++-- packages/store/src/schemas/AddressArray.sol | 4 +- packages/store/src/schemas/CallbackArray.sol | 4 +- packages/store/src/schemas/Mixed.sol | 12 ++--- packages/store/src/schemas/Route.sol | 8 ++-- packages/store/src/schemas/Vector2.sol | 6 +-- packages/store/test/Buffer.t.sol | 34 ++++++++------ packages/store/test/Bytes.t.sol | 2 +- packages/store/test/Gas.t.sol | 2 +- packages/store/test/Storage.t.sol | 6 +-- packages/store/test/StoreCore.t.sol | 48 ++++++++++++-------- 11 files changed, 72 insertions(+), 62 deletions(-) diff --git a/packages/store/src/Bytes.sol b/packages/store/src/Bytes.sol index 859c582b0b..84bfb50dfe 100644 --- a/packages/store/src/Bytes.sol +++ b/packages/store/src/Bytes.sol @@ -125,19 +125,19 @@ library Bytes { // Needs unique name to avoid conflict with `from(uint32)` function fromUint8(uint8 input) internal pure returns (bytes memory output) { - return bytes.concat(bytes1(input)); + return abi.encodePacked(input); } function from(uint32 input) internal pure returns (bytes memory output) { - return bytes.concat(bytes4(input)); + return abi.encodePacked(input); } function from(address input) internal pure returns (bytes memory output) { - return bytes.concat(bytes20(input)); + return abi.encodePacked(input); } function from(bytes4 input) internal pure returns (bytes memory output) { - return bytes.concat(input); + return abi.encodePacked(input); } /************************************************************************ diff --git a/packages/store/src/schemas/AddressArray.sol b/packages/store/src/schemas/AddressArray.sol index a7e5c8b87b..73566f12d1 100644 --- a/packages/store/src/schemas/AddressArray.sol +++ b/packages/store/src/schemas/AddressArray.sol @@ -39,7 +39,7 @@ library AddressArray_ { bytes32 key, address[] memory addresses ) internal { - bytes memory data = bytes.concat(Bytes.from(addresses)); + bytes memory data = Bytes.from(addresses); bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = key; StoreSwitch.setField(tableId, keyTuple, 0, data); @@ -56,7 +56,7 @@ library AddressArray_ { ) internal { bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = key; - bytes memory addresses = bytes.concat(StoreSwitch.getField(tableId, keyTuple, 0), bytes20(addr)); + bytes memory addresses = abi.encodePacked(StoreSwitch.getField(tableId, keyTuple, 0), addr); StoreSwitch.setField(tableId, keyTuple, 0, addresses); } diff --git a/packages/store/src/schemas/CallbackArray.sol b/packages/store/src/schemas/CallbackArray.sol index a6575f0235..21adb02b9c 100644 --- a/packages/store/src/schemas/CallbackArray.sol +++ b/packages/store/src/schemas/CallbackArray.sol @@ -39,7 +39,7 @@ library CallbackArray_ { bytes32 key, bytes24[] memory callbacks ) internal { - bytes memory data = bytes.concat(Bytes.from(callbacks)); + bytes memory data = Bytes.from(callbacks); bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = key; StoreSwitch.setField(tableId, keyTuple, 0, data); @@ -56,7 +56,7 @@ library CallbackArray_ { ) internal { bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = key; - bytes memory callbacks = bytes.concat(StoreSwitch.getField(tableId, keyTuple, 0), callback); + bytes memory callbacks = abi.encodePacked(StoreSwitch.getField(tableId, keyTuple, 0), callback); StoreSwitch.setField(tableId, keyTuple, 0, callbacks); } diff --git a/packages/store/src/schemas/Mixed.sol b/packages/store/src/schemas/Mixed.sol index 1b7ed0d8be..66d1e41f87 100644 --- a/packages/store/src/schemas/Mixed.sol +++ b/packages/store/src/schemas/Mixed.sol @@ -48,13 +48,7 @@ library Mixed_ { ) internal { PackedCounter encodedLengths = PackedCounterLib.pack(uint16(a32.length * 4), uint16(Bytes.from(s).length)); - bytes memory data = bytes.concat( - bytes4(u32), - bytes16(u128), - encodedLengths.unwrap(), - Bytes.from(a32), - Bytes.from(s) - ); + bytes memory data = abi.encodePacked(u32, u128, encodedLengths.unwrap(), Bytes.from(a32), Bytes.from(s)); bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = key; @@ -77,7 +71,7 @@ library Mixed_ { ) internal { bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = key; - StoreSwitch.setField(tableId, keyTuple, 0, bytes.concat(bytes4(u32))); + StoreSwitch.setField(tableId, keyTuple, 0, abi.encodePacked(u32)); } function setU128( @@ -87,7 +81,7 @@ library Mixed_ { ) internal { bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = key; - StoreSwitch.setField(tableId, keyTuple, 1, bytes.concat(bytes16(u128))); + StoreSwitch.setField(tableId, keyTuple, 1, abi.encodePacked(u128)); } function setA32( diff --git a/packages/store/src/schemas/Route.sol b/packages/store/src/schemas/Route.sol index 8d49075a65..6e07ab5984 100644 --- a/packages/store/src/schemas/Route.sol +++ b/packages/store/src/schemas/Route.sol @@ -43,7 +43,7 @@ library Route_ { bytes4 selector, uint8 executionMode ) internal { - bytes memory data = bytes.concat(bytes20(addr), bytes4(selector), bytes1(executionMode)); + bytes memory data = abi.encodePacked(addr, selector, executionMode); bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = key; StoreSwitch.setRecord(tableId, keyTuple, data); @@ -64,7 +64,7 @@ library Route_ { ) internal { bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = key; - StoreSwitch.setField(tableId, keyTuple, 0, bytes.concat(bytes20(addr))); + StoreSwitch.setField(tableId, keyTuple, 0, abi.encodePacked(addr)); } function setSelector( @@ -74,7 +74,7 @@ library Route_ { ) internal { bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = key; - StoreSwitch.setField(tableId, keyTuple, 1, bytes.concat(bytes4(selector))); + StoreSwitch.setField(tableId, keyTuple, 1, abi.encodePacked(selector)); } function setExecutionMode( @@ -84,7 +84,7 @@ library Route_ { ) internal { bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = key; - StoreSwitch.setField(tableId, keyTuple, 2, bytes.concat(bytes1(executionMode))); + StoreSwitch.setField(tableId, keyTuple, 2, abi.encodePacked(executionMode)); } /** Get the table's data */ diff --git a/packages/store/src/schemas/Vector2.sol b/packages/store/src/schemas/Vector2.sol index b3912e647e..64c3648f58 100644 --- a/packages/store/src/schemas/Vector2.sol +++ b/packages/store/src/schemas/Vector2.sol @@ -41,7 +41,7 @@ library Vector2_ { uint32 x, uint32 y ) internal { - bytes memory data = bytes.concat(bytes4(x), bytes4(y)); + bytes memory data = abi.encodePacked(x, y); bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = key; StoreSwitch.setRecord(tableId, keyTuple, data); @@ -62,7 +62,7 @@ library Vector2_ { ) internal { bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = key; - StoreSwitch.setField(tableId, keyTuple, 0, bytes.concat(bytes4(x))); + StoreSwitch.setField(tableId, keyTuple, 0, abi.encodePacked(x)); } function setY( @@ -72,7 +72,7 @@ library Vector2_ { ) internal { bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = key; - StoreSwitch.setField(tableId, keyTuple, 1, bytes.concat(bytes4(y))); + StoreSwitch.setField(tableId, keyTuple, 1, abi.encodePacked(y)); } /** Get the table's data */ diff --git a/packages/store/test/Buffer.t.sol b/packages/store/test/Buffer.t.sol index 80f17f5502..e081a61480 100644 --- a/packages/store/test/Buffer.t.sol +++ b/packages/store/test/Buffer.t.sol @@ -24,7 +24,7 @@ contract BufferTest is Test { } function testFromBytes() public { - bytes memory data = bytes.concat(bytes8(0x0102030405060708)); + bytes memory data = abi.encodePacked(bytes8(0x0102030405060708)); // !gasreport create a buffer from 8 bytes Buffer buf = Buffer_.fromBytes(data); @@ -68,8 +68,8 @@ contract BufferTest is Test { function testAppend() public { Buffer buf = Buffer_.allocate(32); - bytes memory data1 = bytes.concat(bytes8(0x0102030405060708)); - bytes memory data2 = bytes.concat(bytes8(0x090a0b0c0d0e0f10)); + bytes memory data1 = abi.encodePacked(bytes8(0x0102030405060708)); + bytes memory data2 = abi.encodePacked(bytes8(0x090a0b0c0d0e0f10)); // !gasreport append unchecked bytes memory (8) to buffer buf.appendUnchecked(data1); @@ -95,9 +95,9 @@ contract BufferTest is Test { } function testCompareGasToConcat() public { - bytes memory data1 = bytes.concat(keccak256("data1")); - bytes memory data2 = bytes.concat(keccak256("data2")); - bytes memory data3 = bytes.concat(keccak256("data3")); + bytes memory data1 = abi.encodePacked(keccak256("data1")); + bytes memory data2 = abi.encodePacked(keccak256("data2")); + bytes memory data3 = abi.encodePacked(keccak256("data3")); // !gasreport concat 3 bytes memory (32) using buffer Buffer buf = Buffer_.concat(data1, data2, data3); @@ -105,7 +105,11 @@ contract BufferTest is Test { // !gasreport concat 3 bytes memory (32) using bytes.concat bytes memory concat = bytes.concat(data1, data2, data3); + // !gasreport concat 3 bytes memory (32) using abi.encodePacked + bytes memory concat2 = abi.encodePacked(data1, data2, data3); + assertEq(keccak256(buf.toBytes()), keccak256(concat)); + assertEq(keccak256(buf.toBytes()), keccak256(concat2)); } function testAppendFixed() public { @@ -126,9 +130,9 @@ contract BufferTest is Test { function testToBytes() public { Buffer buf = Buffer_.allocate(32); - bytes memory data1 = bytes.concat(bytes8(0x0102030405060708)); - bytes memory data2 = bytes.concat(bytes8(0x090a0b0c0d0e0f10)); - bytes memory data = bytes.concat(data1, data2); + bytes memory data1 = abi.encodePacked(bytes8(0x0102030405060708)); + bytes memory data2 = abi.encodePacked(bytes8(0x090a0b0c0d0e0f10)); + bytes memory data = abi.encodePacked(data1, data2); buf.append(data1); buf.append(data2); @@ -141,7 +145,7 @@ contract BufferTest is Test { function testRead() public { Buffer buf = Buffer_.allocate(32); - bytes memory data = bytes.concat(bytes8(0x0102030405060708)); + bytes memory data = abi.encodePacked(bytes8(0x0102030405060708)); buf.append(data); // !gasreport read bytes32 from buffer @@ -162,7 +166,7 @@ contract BufferTest is Test { function testToArrayUint32() public { Buffer buf = Buffer_.allocate(32); - bytes memory data1 = bytes.concat(bytes8(0x0102030405060708)); + bytes memory data1 = abi.encodePacked(bytes8(0x0102030405060708)); buf.append(data1); // !gasreport buffer toArray with element length 4 @@ -178,7 +182,7 @@ contract BufferTest is Test { function testToArrayUint256() public { Buffer buf = Buffer_.allocate(8); - bytes memory data = bytes.concat(bytes8(0x0102030405060708)); + bytes memory data = abi.encodePacked(bytes8(0x0102030405060708)); buf.append(data); uint256 arrayPtr = buf.toArray(4); @@ -193,7 +197,7 @@ contract BufferTest is Test { function testToArrayUint256TrailingData() public { Buffer buf = Buffer_.allocate(10); - bytes memory data = bytes.concat(bytes8(0x0102030405060708), bytes2(0x090a)); + bytes memory data = abi.encodePacked(bytes8(0x0102030405060708), bytes2(0x090a)); buf.append(data); uint256 arrayPtr = buf.toArray(4); uint256[] memory arr = Cast.toUint256Array(arrayPtr); @@ -206,13 +210,13 @@ contract BufferTest is Test { function testSlice() public { Buffer buf = Buffer_.allocate(32); - bytes memory data = bytes.concat(bytes8(0x0102030405060708)); + bytes memory data = abi.encodePacked(bytes8(0x0102030405060708)); buf.append(data); // !gasreport slice 4 bytes from buffer with offset 4 bytes memory slice = buf.slice(4, 4); assertEq(uint256(slice.length), 4); - assertEq(keccak256(slice), keccak256(bytes.concat(bytes4(0x05060708)))); + assertEq(keccak256(slice), keccak256(abi.encodePacked(bytes4(0x05060708)))); } } diff --git a/packages/store/test/Bytes.t.sol b/packages/store/test/Bytes.t.sol index 26f867d7ba..67b52cfcbf 100644 --- a/packages/store/test/Bytes.t.sol +++ b/packages/store/test/Bytes.t.sol @@ -305,7 +305,7 @@ contract BytesTest is Test { function testSlice32() public { bytes32 original = keccak256("some data"); - bytes memory input = bytes.concat(bytes10(keccak256("irrelevant data")), original); + bytes memory input = abi.encodePacked(bytes10(keccak256("irrelevant data")), original); // !gasreport slice bytes32 with offset 10 bytes32 output = Bytes.slice32(input, 10); diff --git a/packages/store/test/Gas.t.sol b/packages/store/test/Gas.t.sol index 01d2a96f8e..323b42dc80 100644 --- a/packages/store/test/Gas.t.sol +++ b/packages/store/test/Gas.t.sol @@ -50,7 +50,7 @@ contract GasTest is Test { } function customEncode(Mixed memory mixed) pure returns (bytes memory) { - return bytes.concat(bytes4(mixed.u32), bytes16(mixed.u128), Bytes.from(mixed.a32), Bytes.from(mixed.s)); + return abi.encodePacked(mixed.u32, mixed.u128, Bytes.from(mixed.a32), Bytes.from(mixed.s)); } function customDecode(bytes memory input) view returns (Mixed memory) { diff --git a/packages/store/test/Storage.t.sol b/packages/store/test/Storage.t.sol index 435f28a52a..199e1c8faa 100644 --- a/packages/store/test/Storage.t.sol +++ b/packages/store/test/Storage.t.sol @@ -9,16 +9,16 @@ import { Bytes } from "../src/Bytes.sol"; contract StorageTest is Test { function testStoreLoad() public { - bytes memory data1 = bytes.concat( + bytes memory data1 = abi.encodePacked( bytes1(0x01), bytes32(0x0200000000000000000000000000000000000000000000000000000000000003), bytes1(0x04) ); - bytes memory originalDataFirstSlot = bytes.concat( + bytes memory originalDataFirstSlot = abi.encodePacked( bytes32(0x42000000000000000000000000000000000000000000000000000000000069FF) ); - bytes memory originalDataLastSlot = bytes.concat( + bytes memory originalDataLastSlot = abi.encodePacked( bytes32(0xFF42000000000000000000000000000000000000000000000000000000000069) ); diff --git a/packages/store/test/StoreCore.t.sol b/packages/store/test/StoreCore.t.sol index cd17e4cf45..11cd247d76 100644 --- a/packages/store/test/StoreCore.t.sol +++ b/packages/store/test/StoreCore.t.sol @@ -143,7 +143,7 @@ contract StoreCoreTest is Test, StoreView { StoreCore.registerSchema(table, schema); // Set data - bytes memory data = bytes.concat(bytes1(0x01), bytes2(0x0203), bytes1(0x04), bytes2(0x0506)); + bytes memory data = abi.encodePacked(bytes1(0x01), bytes2(0x0203), bytes1(0x04), bytes2(0x0506)); bytes32[] memory key = new bytes32[](1); key[0] = keccak256("some.key"); @@ -165,7 +165,7 @@ contract StoreCoreTest is Test, StoreView { StoreCore.registerSchema(table, schema); // Set data - bytes memory data = bytes.concat(bytes1(0x01), bytes2(0x0203), bytes1(0x04)); + bytes memory data = abi.encodePacked(bytes1(0x01), bytes2(0x0203), bytes1(0x04)); bytes32[] memory key = new bytes32[](1); key[0] = keccak256("some.key"); @@ -181,7 +181,7 @@ contract StoreCoreTest is Test, StoreView { StoreCore.registerSchema(table, schema); // Set data - bytes memory data = bytes.concat( + bytes memory data = abi.encodePacked( bytes16(0x0102030405060708090a0b0c0d0e0f10), bytes32(0x1112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f30) ); @@ -236,7 +236,12 @@ contract StoreCoreTest is Test, StoreView { } // Concat data - bytes memory data = bytes.concat(firstDataBytes, encodedDynamicLength.unwrap(), secondDataBytes, thirdDataBytes); + bytes memory data = abi.encodePacked( + firstDataBytes, + encodedDynamicLength.unwrap(), + secondDataBytes, + thirdDataBytes + ); // Create key bytes32[] memory key = new bytes32[](1); @@ -291,7 +296,7 @@ contract StoreCoreTest is Test, StoreView { // Set first field // !gasreport set static field (1 slot) - StoreCore.setField(table, key, 0, bytes.concat(firstDataBytes)); + StoreCore.setField(table, key, 0, abi.encodePacked(firstDataBytes)); //////////////// // Static data @@ -312,7 +317,7 @@ contract StoreCoreTest is Test, StoreView { bytes32 secondDataBytes = keccak256("some data"); // !gasreport set static field (overlap 2 slot) - StoreCore.setField(table, key, 1, bytes.concat(secondDataBytes)); + StoreCore.setField(table, key, 1, abi.encodePacked(secondDataBytes)); // Get second field // !gasreport get static field (overlap 2 slot) @@ -331,7 +336,7 @@ contract StoreCoreTest is Test, StoreView { assertEq(Bytes.slice32(StoreCoreInternal._getStaticData(table, key), 16), secondDataBytes); assertEq( keccak256(StoreCoreInternal._getStaticData(table, key)), - keccak256(bytes.concat(firstDataBytes, secondDataBytes)) + keccak256(abi.encodePacked(firstDataBytes, secondDataBytes)) ); //////////////// @@ -391,7 +396,9 @@ contract StoreCoreTest is Test, StoreView { PackedCounter encodedLengths = PackedCounterLib.pack(uint16(thirdDataBytes.length), uint16(fourthDataBytes.length)); assertEq( keccak256(StoreCore.getRecord(table, key)), - keccak256(bytes.concat(firstDataBytes, secondDataBytes, encodedLengths.unwrap(), thirdDataBytes, fourthDataBytes)) + keccak256( + abi.encodePacked(firstDataBytes, secondDataBytes, encodedLengths.unwrap(), thirdDataBytes, fourthDataBytes) + ) ); } @@ -430,7 +437,12 @@ contract StoreCoreTest is Test, StoreView { } // Concat data - bytes memory data = bytes.concat(firstDataBytes, encodedDynamicLength.unwrap(), secondDataBytes, thirdDataBytes); + bytes memory data = abi.encodePacked( + firstDataBytes, + encodedDynamicLength.unwrap(), + secondDataBytes, + thirdDataBytes + ); // Create key bytes32[] memory key = new bytes32[](1); @@ -492,7 +504,7 @@ contract StoreCoreTest is Test, StoreView { // !gasreport register subscriber StoreCore.registerHook(table, subscriber); - bytes memory data = bytes.concat(bytes16(0x0102030405060708090a0b0c0d0e0f10)); + bytes memory data = abi.encodePacked(bytes16(0x0102030405060708090a0b0c0d0e0f10)); // !gasreport set record on table with subscriber StoreCore.setRecord(table, key, data); @@ -501,7 +513,7 @@ contract StoreCoreTest is Test, StoreView { bytes memory indexedData = StoreCore.getRecord(indexerTableId, key); assertEq(keccak256(data), keccak256(indexedData)); - data = bytes.concat(bytes16(0x1112131415161718191a1b1c1d1e1f20)); + data = abi.encodePacked(bytes16(0x1112131415161718191a1b1c1d1e1f20)); // !gasreport set static field on table with subscriber StoreCore.setField(table, key, 0, data); @@ -515,7 +527,7 @@ contract StoreCoreTest is Test, StoreView { // Get data from indexed table - the indexer should have mirrored the data there indexedData = StoreCore.getRecord(indexerTableId, key); - assertEq(keccak256(indexedData), keccak256(bytes.concat(bytes16(0)))); + assertEq(keccak256(indexedData), keccak256(abi.encodePacked(bytes16(0)))); } function testHooksDynamicData() public { @@ -537,9 +549,9 @@ contract StoreCoreTest is Test, StoreView { arrayData[0] = 0x01020304; bytes memory arrayDataBytes = Bytes.from(arrayData); PackedCounter encodedArrayDataLength = PackedCounterLib.pack(uint16(arrayDataBytes.length)); - bytes memory dynamicData = bytes.concat(encodedArrayDataLength.unwrap(), arrayDataBytes); - bytes memory staticData = bytes.concat(bytes16(0x0102030405060708090a0b0c0d0e0f10)); - bytes memory data = bytes.concat(staticData, dynamicData); + bytes memory dynamicData = abi.encodePacked(encodedArrayDataLength.unwrap(), arrayDataBytes); + bytes memory staticData = abi.encodePacked(bytes16(0x0102030405060708090a0b0c0d0e0f10)); + bytes memory data = abi.encodePacked(staticData, dynamicData); // !gasreport set (dynamic) record on table with subscriber StoreCore.setRecord(table, key, data); @@ -551,8 +563,8 @@ contract StoreCoreTest is Test, StoreView { // Update dynamic data arrayData[0] = 0x11121314; arrayDataBytes = Bytes.from(arrayData); - dynamicData = bytes.concat(encodedArrayDataLength.unwrap(), arrayDataBytes); - data = bytes.concat(staticData, dynamicData); + dynamicData = abi.encodePacked(encodedArrayDataLength.unwrap(), arrayDataBytes); + data = abi.encodePacked(staticData, dynamicData); // !gasreport set (dynamic) field on table with subscriber StoreCore.setField(table, key, 1, arrayDataBytes); @@ -566,7 +578,7 @@ contract StoreCoreTest is Test, StoreView { // Get data from indexed table - the indexer should have mirrored the data there indexedData = StoreCore.getRecord(indexerTableId, key); - assertEq(keccak256(indexedData), keccak256(bytes.concat(bytes16(0)))); + assertEq(keccak256(indexedData), keccak256(abi.encodePacked(bytes16(0)))); } } From 73e8f2ff901d47fd7777b61503b59b01a6bba974 Mon Sep 17 00:00:00 2001 From: alvrs Date: Thu, 2 Feb 2023 11:04:08 +0000 Subject: [PATCH 69/82] test: update gas report --- packages/store/gas-report.txt | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/store/gas-report.txt b/packages/store/gas-report.txt index 31d5871c3f..f7142b9e32 100644 --- a/packages/store/gas-report.txt +++ b/packages/store/gas-report.txt @@ -8,6 +8,7 @@ (test/Buffer.t.sol) | append bytes8 of bytes32 to buffer [buf.append(data2, 8)]: 645 (test/Buffer.t.sol) | concat 3 bytes memory (32) using buffer [Buffer buf = Buffer_.concat(data1, data2, data3)]: 2638 (test/Buffer.t.sol) | concat 3 bytes memory (32) using bytes.concat [bytes memory concat = bytes.concat(data1, data2, data3)]: 641 +(test/Buffer.t.sol) | concat 3 bytes memory (32) using abi.encodePacked [bytes memory concat2 = abi.encodePacked(data1, data2, data3)]: 641 (test/Buffer.t.sol) | create a buffer from 8 bytes [Buffer buf = Buffer_.fromBytes(data)]: 40 (test/Buffer.t.sol) | read bytes32 from buffer [bytes32 value = buf.read32(4)]: 102 (test/Buffer.t.sol) | read bytes8 with offset 3 from buffer [bytes8 value2 = buf.read8(3)]: 151 @@ -55,7 +56,7 @@ (test/Gas.t.sol) | pass custom encoded bytes to external contract [someContract.doSomethingWithBytes(customEncoded)]: 1342 (test/MixedTable.t.sol) | store Mixed struct in storage (native solidity) [testMixed = mixed]: 92016 (test/MixedTable.t.sol) | register MixedTable schema [MixedTable.registerSchema()]: 32531 -(test/MixedTable.t.sol) | set record in MixedTable [MixedTable.set({ key: key, u32: 1, u128: 2, a32: a32, s: s })]: 114470 +(test/MixedTable.t.sol) | set record in MixedTable [MixedTable.set({ key: key, u32: 1, u128: 2, a32: a32, s: s })]: 114455 (test/MixedTable.t.sol) | get record from MixedTable [Mixed memory mixed = MixedTable.get(key)]: 20506 (test/PackedCounter.t.sol) | get value at index of PackedCounter [packedCounter.atIndex(3)]: 272 (test/PackedCounter.t.sol) | set value at index of PackedCounter [packedCounter = packedCounter.setAtIndex(2, 5)]: 884 @@ -98,10 +99,10 @@ (test/StoreCore.t.sol) | set dynamic length of dynamic index 0 [StoreCoreInternal._setDynamicDataLengthAtIndex(table, key, 0, 10)]: 23719 (test/StoreCore.t.sol) | set dynamic length of dynamic index 1 [StoreCoreInternal._setDynamicDataLengthAtIndex(table, key, 1, 99)]: 1820 (test/StoreCore.t.sol) | reduce dynamic length of dynamic index 0 [StoreCoreInternal._setDynamicDataLengthAtIndex(table, key, 0, 5)]: 1821 -(test/StoreCore.t.sol) | set static field (1 slot) [StoreCore.setField(table, key, 0, bytes.concat(firstDataBytes))]: 38105 -(test/StoreCore.t.sol) | get static field (1 slot) [bytes memory loadedData = StoreCore.getField(table, key, 0)]: 4595 -(test/StoreCore.t.sol) | set static field (overlap 2 slot) [StoreCore.setField(table, key, 1, bytes.concat(secondDataBytes))]: 33895 -(test/StoreCore.t.sol) | get static field (overlap 2 slot) [loadedData = StoreCore.getField(table, key, 1)]: 6379 +(test/StoreCore.t.sol) | set static field (1 slot) [StoreCore.setField(table, key, 0, abi.encodePacked(firstDataBytes))]: 38105 +(test/StoreCore.t.sol) | get static field (1 slot) [bytes memory loadedData = StoreCore.getField(table, key, 0)]: 4592 +(test/StoreCore.t.sol) | set static field (overlap 2 slot) [StoreCore.setField(table, key, 1, abi.encodePacked(secondDataBytes))]: 33895 +(test/StoreCore.t.sol) | get static field (overlap 2 slot) [loadedData = StoreCore.getField(table, key, 1)]: 6375 (test/StoreCore.t.sol) | set dynamic field (1 slot, first dynamic field) [StoreCore.setField(table, key, 2, thirdDataBytes)]: 57420 (test/StoreCore.t.sol) | get dynamic field (1 slot, first dynamic field) [loadedData = StoreCore.getField(table, key, 2)]: 5357 (test/StoreCore.t.sol) | set dynamic field (1 slot, second dynamic field) [StoreCore.setField(table, key, 3, fourthDataBytes)]: 35554 From 28ed69c8888ea5e71c8cc92e61c4f47759d0a954 Mon Sep 17 00:00:00 2001 From: alvrs Date: Thu, 2 Feb 2023 12:31:05 +0100 Subject: [PATCH 70/82] refactor(core): change argument name of registerHook --- packages/store/src/StoreCore.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/store/src/StoreCore.sol b/packages/store/src/StoreCore.sol index 963b02f096..4a4b386d71 100644 --- a/packages/store/src/StoreCore.sol +++ b/packages/store/src/StoreCore.sol @@ -78,8 +78,8 @@ library StoreCore { /* * Register hooks to be called when a record or field is set or deleted */ - function registerHook(bytes32 table, IStoreHook hooks) external { - HooksTable.push(table, address(hooks)); + function registerHook(bytes32 table, IStoreHook hook) external { + HooksTable.push(table, address(hook)); } /************************************************************************ From 0c22b76d02266212e9c885c1f6eaa11733abc7e8 Mon Sep 17 00:00:00 2001 From: alvrs Date: Thu, 2 Feb 2023 14:16:37 +0100 Subject: [PATCH 71/82] fix(cli): support gas-report with lines including quotes --- packages/cli/src/commands/gas-report.ts | 2 +- packages/cli/tsconfig.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/commands/gas-report.ts b/packages/cli/src/commands/gas-report.ts index 89a723cd5b..f66c23a4e1 100644 --- a/packages/cli/src/commands/gas-report.ts +++ b/packages/cli/src/commands/gas-report.ts @@ -136,7 +136,7 @@ async function runGasReport(path: string): Promise { _gasreport = gasleft(); ${functionCall} _gasreport = _gasreport - gasleft(); -console.log("GAS REPORT: ${name} [${functionCall}]:", _gasreport);` +console.log("GAS REPORT: ${name} [${functionCall.replaceAll('"', '\\"')}]:", _gasreport);` ); i++; diff --git a/packages/cli/tsconfig.json b/packages/cli/tsconfig.json index df5a65239a..5a15b828a1 100644 --- a/packages/cli/tsconfig.json +++ b/packages/cli/tsconfig.json @@ -11,7 +11,7 @@ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ /* Language and Environment */ - "target": "es2015" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, + "target": "es2021" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ // "jsx": "preserve", /* Specify what JSX code is generated. */ // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ From c650b1c82accbe9808475d9affba7d79a445d4a2 Mon Sep 17 00:00:00 2001 From: alvrs Date: Mon, 6 Feb 2023 20:04:12 -0500 Subject: [PATCH 72/82] chore: remove unnecessary conversion methods for primitive types --- packages/store/src/Bytes.sol | 23 --------- packages/store/src/schemas/Mixed.sol | 6 +-- packages/store/test/Bytes.t.sol | 72 ---------------------------- packages/store/test/Gas.t.sol | 2 +- 4 files changed, 4 insertions(+), 99 deletions(-) diff --git a/packages/store/src/Bytes.sol b/packages/store/src/Bytes.sol index 84bfb50dfe..ebbb801239 100644 --- a/packages/store/src/Bytes.sol +++ b/packages/store/src/Bytes.sol @@ -73,12 +73,6 @@ library Bytes { return _from(ptr, 24, true); } - function from(string memory input) internal pure returns (bytes memory output) { - assembly { - output := input - } - } - function from(address[] memory input) internal pure returns (bytes memory output) { bytes32 ptr; assembly { @@ -123,23 +117,6 @@ library Bytes { } } - // Needs unique name to avoid conflict with `from(uint32)` - function fromUint8(uint8 input) internal pure returns (bytes memory output) { - return abi.encodePacked(input); - } - - function from(uint32 input) internal pure returns (bytes memory output) { - return abi.encodePacked(input); - } - - function from(address input) internal pure returns (bytes memory output) { - return abi.encodePacked(input); - } - - function from(bytes4 input) internal pure returns (bytes memory output) { - return abi.encodePacked(input); - } - /************************************************************************ * * BYTES -> ANYTHING diff --git a/packages/store/src/schemas/Mixed.sol b/packages/store/src/schemas/Mixed.sol index 66d1e41f87..103cf00207 100644 --- a/packages/store/src/schemas/Mixed.sol +++ b/packages/store/src/schemas/Mixed.sol @@ -46,9 +46,9 @@ library Mixed_ { uint32[] memory a32, string memory s ) internal { - PackedCounter encodedLengths = PackedCounterLib.pack(uint16(a32.length * 4), uint16(Bytes.from(s).length)); + PackedCounter encodedLengths = PackedCounterLib.pack(uint16(a32.length * 4), uint16(bytes(s).length)); - bytes memory data = abi.encodePacked(u32, u128, encodedLengths.unwrap(), Bytes.from(a32), Bytes.from(s)); + bytes memory data = abi.encodePacked(u32, u128, encodedLengths.unwrap(), Bytes.from(a32), bytes(s)); bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = key; @@ -101,7 +101,7 @@ library Mixed_ { ) internal { bytes32[] memory keyTuple = new bytes32[](1); keyTuple[0] = key; - StoreSwitch.setField(tableId, keyTuple, 3, Bytes.from(s)); + StoreSwitch.setField(tableId, keyTuple, 3, bytes(s)); } /** Get the table's data */ diff --git a/packages/store/test/Bytes.t.sol b/packages/store/test/Bytes.t.sol index 67b52cfcbf..87775a5365 100644 --- a/packages/store/test/Bytes.t.sol +++ b/packages/store/test/Bytes.t.sol @@ -160,78 +160,6 @@ contract BytesTest is Test { assertEq(uint256(output[2]), 0x0500000000000000000000000000000000000000000000000000000000000000); } - function testFromAndToUint32() public { - uint32 input = 0x01000002; - - // !gasreport create bytes from uint32 - bytes memory output = Bytes.from(input); - - assertEq(output.length, 4); - - // !gasreport create uint32 from bytes - uint32 output2 = Bytes.toUint32(output); - - assertEq(output2, input); - } - - function testFromAndToUint32Fuzzy(uint32 input) public { - assertEq(Bytes.toUint32(Bytes.from(input)), input); - } - - function testFromAndToAddress() public { - address input = address(0x0100000000000000000000000000000000000002); - - // !gasreport create bytes from address - bytes memory output = Bytes.from(input); - - assertEq(output.length, 20); - - // !gasreport create address from bytes - address output2 = Bytes.toAddress(output); - - assertEq(output2, input); - } - - function testFromAndToAddressFuzzy(address input) public { - assertEq(Bytes.toAddress(Bytes.from(input)), input); - } - - function testFromAndToUint8() public { - uint8 input = 0x02; - - // !gasreport create bytes from uint8 - bytes memory output = Bytes.fromUint8(input); - - assertEq(output.length, 1); - - // !gasreport create uint8 from bytes - uint8 output2 = Bytes.toUint8(output); - - assertEq(output2, input); - } - - function testFromAndToUint8Fuzzy(uint8 input) public { - assertEq(Bytes.toUint8(Bytes.fromUint8(input)), input); - } - - function testFromAndToBytes4() public { - bytes4 input = bytes4(0x01000002); - - // !gasreport create bytes from bytes4 - bytes memory output = Bytes.from(input); - - assertEq(output.length, 4); - - // !gasreport create bytes4 from bytes - bytes4 output2 = Bytes.toBytes4(output); - - assertEq(output2, input); - } - - function testFromAndToBytes4Fuzzy(bytes4 input) public { - assertEq(Bytes.toBytes4(Bytes.from(input)), input); - } - function testEquals() public { bytes memory a = bytes("a"); bytes memory b = bytes("a"); diff --git a/packages/store/test/Gas.t.sol b/packages/store/test/Gas.t.sol index 323b42dc80..9d2017cb28 100644 --- a/packages/store/test/Gas.t.sol +++ b/packages/store/test/Gas.t.sol @@ -50,7 +50,7 @@ contract GasTest is Test { } function customEncode(Mixed memory mixed) pure returns (bytes memory) { - return abi.encodePacked(mixed.u32, mixed.u128, Bytes.from(mixed.a32), Bytes.from(mixed.s)); + return abi.encodePacked(mixed.u32, mixed.u128, Bytes.from(mixed.a32), mixed.s); } function customDecode(bytes memory input) view returns (Mixed memory) { From 40f33c2aeee4206d0b69688eeb4d4691b5a82309 Mon Sep 17 00:00:00 2001 From: alvrs Date: Mon, 6 Feb 2023 20:12:30 -0500 Subject: [PATCH 73/82] chore(store): remove unused code --- packages/store/src/Bytes.sol | 23 ----------------------- packages/store/test/Bytes.t.sol | 10 ---------- 2 files changed, 33 deletions(-) diff --git a/packages/store/src/Bytes.sol b/packages/store/src/Bytes.sol index ebbb801239..a4a4b4c7e9 100644 --- a/packages/store/src/Bytes.sol +++ b/packages/store/src/Bytes.sol @@ -216,13 +216,6 @@ library Bytes { return keccak256(a) == keccak256(b); } - function setLengthInPlace(bytes memory input, uint256 length) internal pure returns (bytes memory) { - assembly { - mstore(input, length) - } - return input; - } - /************************************************************************ * * SET @@ -663,20 +656,4 @@ library Bytes { } return output; } - - /** - * Splits a `bytes` memory array into a `bytes` memory array of the given lengths. - */ - function split(bytes memory data, uint256[] memory lengths) internal pure returns (bytes[] memory chunks) { - chunks = new bytes[](lengths.length); - uint256 sum; - for (uint256 i = 0; i < lengths.length; ) { - chunks[i] = slice(data, sum, lengths[i]); - unchecked { - sum += lengths[i]; - i += 1; - } - } - return chunks; - } } diff --git a/packages/store/test/Bytes.t.sol b/packages/store/test/Bytes.t.sol index 87775a5365..9ae548a3d9 100644 --- a/packages/store/test/Bytes.t.sol +++ b/packages/store/test/Bytes.t.sol @@ -186,16 +186,6 @@ contract BytesTest is Test { assertFalse(Bytes.equals(a, b)); } - function testSetLengthInPlace() public { - bytes memory a = new bytes(5); - assertEq(a.length, 5); - - // !gasreport set length of bytes in place - Bytes.setLengthInPlace(a, 2); - - assertEq(a.length, 2); - } - function testSlice() public { bytes memory a = new bytes(5); a[0] = 0x01; From 211e72ab6808fe331129fc5d707893b459f33267 Mon Sep 17 00:00:00 2001 From: alvrs Date: Mon, 6 Feb 2023 20:15:12 -0500 Subject: [PATCH 74/82] refactor: use uint256 in PackedCounter accumulator --- packages/store/src/PackedCounter.sol | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/store/src/PackedCounter.sol b/packages/store/src/PackedCounter.sol index 60eb706c28..94ac2abe4c 100644 --- a/packages/store/src/PackedCounter.sol +++ b/packages/store/src/PackedCounter.sol @@ -120,8 +120,11 @@ library PackedCounterLib { uint256 currentValueAtIndex = atIndex(packedCounter, uint8(index)); // Compute the difference and update the total value - int256 lengthDiff = int256(newValueAtIndex) - int256(currentValueAtIndex); - accumulator = uint256(int256(accumulator) + lengthDiff); + if (newValueAtIndex >= currentValueAtIndex) { + accumulator += newValueAtIndex - currentValueAtIndex; + } else { + accumulator -= currentValueAtIndex - newValueAtIndex; + } // Set the new accumulated value and value at index uint256 offset = 4 + index * 2; // (4 bytes total length, 2 bytes per dynamic schema) From 8e97d6e1ba611eba0da8c38ec53114c82f0d316e Mon Sep 17 00:00:00 2001 From: alvrs Date: Mon, 6 Feb 2023 20:15:48 -0500 Subject: [PATCH 75/82] chore: change setting of schema length bytes --- packages/store/src/Schema.sol | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/store/src/Schema.sol b/packages/store/src/Schema.sol index bbbe855243..033cf5d023 100644 --- a/packages/store/src/Schema.sol +++ b/packages/store/src/Schema.sol @@ -59,8 +59,7 @@ library SchemaLib { if (dynamicFields > 14) revert SchemaLib_InvalidLength(dynamicFields); // Store total static length, and number of static and dynamic fields - schema = Bytes.setBytes1(schema, 0, bytes1(bytes2(length))); // upper length byte - schema = Bytes.setBytes1(schema, 1, bytes1(uint8(length))); // lower length byte + schema = Bytes.setBytes2(schema, 0, (bytes2(length))); // 2 length bytes schema = Bytes.setBytes1(schema, 2, bytes1(staticFields)); // number of static fields schema = Bytes.setBytes1(schema, 3, bytes1(dynamicFields)); // number of dynamic fields From daee7806fd7f35866aff3597122985ed8cad49a7 Mon Sep 17 00:00:00 2001 From: alvrs Date: Mon, 6 Feb 2023 20:19:51 -0500 Subject: [PATCH 76/82] fix: don't reset dynamic data when deleting storage value but only length --- packages/store/src/StoreCore.sol | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/packages/store/src/StoreCore.sol b/packages/store/src/StoreCore.sol index 4a4b386d71..eb2245d38c 100644 --- a/packages/store/src/StoreCore.sol +++ b/packages/store/src/StoreCore.sol @@ -204,17 +204,6 @@ library StoreCore { // If there are no dynamic fields, we're done if (schema.numDynamicFields() == 0) return; - // Delete dynamic data - uint256 dynamicDataLocation; - PackedCounter encodedLengths = StoreCoreInternal._loadEncodedDynamicDataLength(table, key); - for (uint8 i; i < schema.numDynamicFields(); ) { - dynamicDataLocation = StoreCoreInternal._getDynamicDataLocation(table, key, i); - Storage.store({ storagePointer: dynamicDataLocation, offset: 0, data: new bytes(encodedLengths.atIndex(i)) }); - unchecked { - i++; - } - } - // Delete dynamic data length uint256 dynamicDataLengthLocation = StoreCoreInternal._getDynamicDataLengthLocation(table, key); Storage.store({ storagePointer: dynamicDataLengthLocation, data: bytes32(0) }); From bf72e069d261cda2a0a31b0ff908b54ed23685fe Mon Sep 17 00:00:00 2001 From: alvrs Date: Mon, 6 Feb 2023 20:21:51 -0500 Subject: [PATCH 77/82] chore: update comments --- packages/store/src/StoreCore.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/store/src/StoreCore.sol b/packages/store/src/StoreCore.sol index eb2245d38c..22f8f4e826 100644 --- a/packages/store/src/StoreCore.sol +++ b/packages/store/src/StoreCore.sol @@ -219,7 +219,7 @@ library StoreCore { ************************************************************************/ /** - * Get full static record for the given table and key tuple (loading schema from storage) + * Get full record (all fields, static and dynamic data) for the given table and key tuple (loading schema from storage) */ function getRecord(bytes32 table, bytes32[] memory key) internal view returns (bytes memory) { // Get schema for this table @@ -230,7 +230,7 @@ library StoreCore { } /** - * Get full data (static and dynamic) for the given table and key tuple, with the given schema + * Get full record (all fields, static and dynamic data) for the given table and key tuple, with the given schema */ function getRecord( bytes32 table, From 972f9f53b653e255c79a69dafb3f816d0ed86f41 Mon Sep 17 00:00:00 2001 From: alvrs Date: Mon, 6 Feb 2023 21:41:36 -0500 Subject: [PATCH 78/82] chore: move _getStaticData into the correct section --- packages/store/src/StoreCore.sol | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/store/src/StoreCore.sol b/packages/store/src/StoreCore.sol index 22f8f4e826..758ef020ba 100644 --- a/packages/store/src/StoreCore.sol +++ b/packages/store/src/StoreCore.sol @@ -374,6 +374,12 @@ library StoreCoreInternal { Storage.store({ storagePointer: dynamicDataLocation, data: data }); } + /************************************************************************ + * + * GET DATA + * + ************************************************************************/ + /** * Get full static record for the given table and key tuple (loading schema from storage) */ @@ -382,12 +388,6 @@ library StoreCoreInternal { return _getStaticData(table, key, schema); } - /************************************************************************ - * - * GET DATA - * - ************************************************************************/ - /** * Get full static data for the given table and key tuple, with the given schema */ From 8debd1cd96ae86db12fb24bbcf38b384fb7f000f Mon Sep 17 00:00:00 2001 From: alvrs Date: Mon, 6 Feb 2023 21:43:08 -0500 Subject: [PATCH 79/82] chore: remove misleading comment --- packages/store/src/StoreCore.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/store/src/StoreCore.sol b/packages/store/src/StoreCore.sol index 758ef020ba..9d511deabc 100644 --- a/packages/store/src/StoreCore.sol +++ b/packages/store/src/StoreCore.sol @@ -461,7 +461,7 @@ library StoreCoreInternal { * Get storage offset for the given schema and (static length) index */ function _getStaticDataOffset(Schema schema, uint8 schemaIndex) internal pure returns (uint256) { - uint256 offset = 0; // skip length + uint256 offset = 0; for (uint256 i = 0; i < schemaIndex; i++) { offset += getStaticByteLength(schema.atIndex(i)); } From 2c5e6f3c0c7b584ba1259843bf31551bde855ba9 Mon Sep 17 00:00:00 2001 From: alvrs Date: Mon, 6 Feb 2023 21:47:07 -0500 Subject: [PATCH 80/82] chore: remove comment --- packages/store/src/System.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/store/src/System.sol b/packages/store/src/System.sol index 3d5dfed74e..1fce49db82 100644 --- a/packages/store/src/System.sol +++ b/packages/store/src/System.sol @@ -1,7 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; -// TODO: Use existing ERC2771 implementations contract System { // Extract the trusted msg.sender value appended to the calldata function _msgSender() internal pure returns (address sender) { From e144272028db0c7db0e5fce1a7a35f5545201714 Mon Sep 17 00:00:00 2001 From: alvrs Date: Mon, 6 Feb 2023 21:48:39 -0500 Subject: [PATCH 81/82] chore: remove comment --- packages/store/src/Utils.sol | 8 -------- 1 file changed, 8 deletions(-) diff --git a/packages/store/src/Utils.sol b/packages/store/src/Utils.sol index 577914fc91..6209c585f9 100644 --- a/packages/store/src/Utils.sol +++ b/packages/store/src/Utils.sol @@ -10,14 +10,6 @@ library Utils { * Adapted from https://github.com/dk1a/solidity-stringutils/blob/main/src/utils/mem.sol#L149-L167 * @dev Left-aligned bit mask (e.g. for partial mload/mstore). * For length >= 32 returns type(uint256).max - * - * length 0: 0x000000...000000 - * length 1: 0xff0000...000000 - * length 2: 0xffff00...000000 - * ... - * length 30: 0xffffff...ff0000 - * length 31: 0xffffff...ffff00 - * length 32+: 0xffffff...ffffff */ function leftMask(uint256 bitLength) internal pure returns (uint256) { unchecked { From 9293a51b77abf80b46bb86c4b9976d90559f8e6e Mon Sep 17 00:00:00 2001 From: alvrs Date: Tue, 7 Feb 2023 03:07:00 +0000 Subject: [PATCH 82/82] test: update gas report --- packages/store/gas-report.txt | 41 ++++++++++++++--------------------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/packages/store/gas-report.txt b/packages/store/gas-report.txt index f7142b9e32..8f1b868e45 100644 --- a/packages/store/gas-report.txt +++ b/packages/store/gas-report.txt @@ -22,15 +22,7 @@ (test/Buffer.t.sol) | buffer (32 bytes) to bytes memory [bytes memory bufferData = buf.toBytes()]: 90 (test/Bytes.t.sol) | compare equal bytes [bool equals = Bytes.equals(a, b)]: 202 (test/Bytes.t.sol) | compare unequal bytes [bool equals = Bytes.equals(a, b)]: 202 -(test/Bytes.t.sol) | create bytes from address [bytes memory output = Bytes.from(input)]: 157 -(test/Bytes.t.sol) | create address from bytes [address output2 = Bytes.toAddress(output)]: 166 -(test/Bytes.t.sol) | create bytes from bytes4 [bytes memory output = Bytes.from(input)]: 151 -(test/Bytes.t.sol) | create bytes4 from bytes [bytes4 output2 = Bytes.toBytes4(output)]: 160 -(test/Bytes.t.sol) | create bytes from uint32 [bytes memory output = Bytes.from(input)]: 149 -(test/Bytes.t.sol) | create uint32 from bytes [uint32 output2 = Bytes.toUint32(output)]: 166 (test/Bytes.t.sol) | create uint32 array from bytes memory [uint32[] memory output = Bytes.toUint32Array(tight)]: 835 -(test/Bytes.t.sol) | create bytes from uint8 [bytes memory output = Bytes.fromUint8(input)]: 160 -(test/Bytes.t.sol) | create uint8 from bytes [uint8 output2 = Bytes.toUint8(output)]: 105 (test/Bytes.t.sol) | create bytes from bytes array [bytes memory output = Bytes.from(input)]: 1290 (test/Bytes.t.sol) | create bytes from uint16 array [bytes memory output = Bytes.from(input)]: 791 (test/Bytes.t.sol) | create bytes from uint32 array [bytes memory output = Bytes.from(input)]: 695 @@ -38,7 +30,6 @@ (test/Bytes.t.sol) | set bytes1 in bytes32 [Bytes.setBytes1(input, 8, 0xff)]: 7 (test/Bytes.t.sol) | set bytes2 in bytes32 [Bytes.setBytes2(input, 8, 0xffff)]: 7 (test/Bytes.t.sol) | set bytes4 in bytes32 [Bytes.setBytes4(input, 8, 0xffffffff)]: 7 -(test/Bytes.t.sol) | set length of bytes in place [Bytes.setLengthInPlace(a, 2)]: 16 (test/Bytes.t.sol) | slice bytes (with copying) with offset 1 and length 3 [bytes memory b = Bytes.slice(a, 1, 3)]: 516 (test/Bytes.t.sol) | slice bytes3 with offset 1 [bytes3 b = Bytes.slice3(a, 1)]: 77 (test/Bytes.t.sol) | slice bytes32 with offset 10 [bytes32 output = Bytes.slice32(input, 10)]: 74 @@ -55,17 +46,17 @@ (test/Gas.t.sol) | pass abi encoded bytes to external contract [someContract.doSomethingWithBytes(abiEncoded)]: 6537 (test/Gas.t.sol) | pass custom encoded bytes to external contract [someContract.doSomethingWithBytes(customEncoded)]: 1342 (test/MixedTable.t.sol) | store Mixed struct in storage (native solidity) [testMixed = mixed]: 92016 -(test/MixedTable.t.sol) | register MixedTable schema [MixedTable.registerSchema()]: 32531 +(test/MixedTable.t.sol) | register MixedTable schema [MixedTable.registerSchema()]: 32496 (test/MixedTable.t.sol) | set record in MixedTable [MixedTable.set({ key: key, u32: 1, u128: 2, a32: a32, s: s })]: 114455 -(test/MixedTable.t.sol) | get record from MixedTable [Mixed memory mixed = MixedTable.get(key)]: 20506 +(test/MixedTable.t.sol) | get record from MixedTable [Mixed memory mixed = MixedTable.get(key)]: 20471 (test/PackedCounter.t.sol) | get value at index of PackedCounter [packedCounter.atIndex(3)]: 272 -(test/PackedCounter.t.sol) | set value at index of PackedCounter [packedCounter = packedCounter.setAtIndex(2, 5)]: 884 +(test/PackedCounter.t.sol) | set value at index of PackedCounter [packedCounter = packedCounter.setAtIndex(2, 5)]: 830 (test/PackedCounter.t.sol) | pack uint16 array into PackedCounter [PackedCounter packedCounter = PackedCounterLib.pack(counters)]: 2148 (test/PackedCounter.t.sol) | get total of PackedCounter [packedCounter.total()]: 33 -(test/RouteTable.t.sol) | register RouteTable schema [RouteTable.registerSchema()]: 30475 +(test/RouteTable.t.sol) | register RouteTable schema [RouteTable.registerSchema()]: 30440 (test/RouteTable.t.sol) | set RouteTable record [RouteTable.set(key, addr, selector, executionMode)]: 35240 (test/RouteTable.t.sol) | get RouteTable record [Route memory systemEntry = RouteTable.get(key)]: 6554 -(test/Schema.t.sol) | encode schema with 6 entries [SchemaLib.encode]: 6202 +(test/Schema.t.sol) | encode schema with 6 entries [SchemaLib.encode]: 6172 (test/Schema.t.sol) | get schema type at index [SchemaType schemaType1 = schema.atIndex(0)]: 200 (test/Schema.t.sol) | get number of dynamic fields from schema [uint256 num = schema.numDynamicFields()]: 80 (test/Schema.t.sol) | get number of static fields from schema [uint256 num = schema.numStaticFields()]: 91 @@ -79,33 +70,33 @@ (test/StoreCore.t.sol) | access non-existing record [bytes memory data1 = StoreCore.getRecord(table, key)]: 10090 (test/StoreCore.t.sol) | access static field of non-existing record [bytes memory data2 = StoreCore.getField(table, key, 0)]: 4505 (test/StoreCore.t.sol) | access dynamic field of non-existing record [bytes memory data3 = StoreCore.getField(table, key, 1)]: 3940 -(test/StoreCore.t.sol) | delete record (complex data, 3 slots) [StoreCore.deleteRecord(table, key)]: 18029 +(test/StoreCore.t.sol) | delete record (complex data, 3 slots) [StoreCore.deleteRecord(table, key)]: 10970 (test/StoreCore.t.sol) | Check for existence of table (existent) [StoreCore.hasTable(table)]: 992 (test/StoreCore.t.sol) | check for existence of table (non-existent) [StoreCore.hasTable(table2)]: 2993 -(test/StoreCore.t.sol) | register subscriber [StoreCore.registerHook(table, subscriber)]: 70377 +(test/StoreCore.t.sol) | register subscriber [StoreCore.registerHook(table, subscriber)]: 70323 (test/StoreCore.t.sol) | set record on table with subscriber [StoreCore.setRecord(table, key, data)]: 72168 (test/StoreCore.t.sol) | set static field on table with subscriber [StoreCore.setField(table, key, 0, data)]: 34254 (test/StoreCore.t.sol) | delete record on table with subscriber [StoreCore.deleteRecord(table, key)]: 24678 -(test/StoreCore.t.sol) | register subscriber [StoreCore.registerHook(table, subscriber)]: 70377 +(test/StoreCore.t.sol) | register subscriber [StoreCore.registerHook(table, subscriber)]: 70323 (test/StoreCore.t.sol) | set (dynamic) record on table with subscriber [StoreCore.setRecord(table, key, data)]: 174918 -(test/StoreCore.t.sol) | set (dynamic) field on table with subscriber [StoreCore.setField(table, key, 1, arrayDataBytes)]: 37111 -(test/StoreCore.t.sol) | delete (dynamic) record on table with subscriber [StoreCore.deleteRecord(table, key)]: 38393 +(test/StoreCore.t.sol) | set (dynamic) field on table with subscriber [StoreCore.setField(table, key, 1, arrayDataBytes)]: 37025 +(test/StoreCore.t.sol) | delete (dynamic) record on table with subscriber [StoreCore.deleteRecord(table, key)]: 30425 (test/StoreCore.t.sol) | StoreCore: register schema [StoreCore.registerSchema(table, schema)]: 26419 (test/StoreCore.t.sol) | StoreCore: get schema (warm) [Schema loadedSchema = StoreCore.getSchema(table)]: 937 (test/StoreCore.t.sol) | set complex record with dynamic data (4 slots) [StoreCore.setRecord(table, key, data)]: 110652 (test/StoreCore.t.sol) | get complex record with dynamic data (4 slots) [bytes memory loadedData = StoreCore.getRecord(table, key)]: 12755 (test/StoreCore.t.sol) | compare: Set complex record with dynamic data using native solidity [testStruct = _testStruct]: 116815 (test/StoreCore.t.sol) | compare: Set complex record with dynamic data using abi.encode [testMapping[1234] = abi.encode(_testStruct)]: 267529 -(test/StoreCore.t.sol) | set dynamic length of dynamic index 0 [StoreCoreInternal._setDynamicDataLengthAtIndex(table, key, 0, 10)]: 23719 -(test/StoreCore.t.sol) | set dynamic length of dynamic index 1 [StoreCoreInternal._setDynamicDataLengthAtIndex(table, key, 1, 99)]: 1820 -(test/StoreCore.t.sol) | reduce dynamic length of dynamic index 0 [StoreCoreInternal._setDynamicDataLengthAtIndex(table, key, 0, 5)]: 1821 +(test/StoreCore.t.sol) | set dynamic length of dynamic index 0 [StoreCoreInternal._setDynamicDataLengthAtIndex(table, key, 0, 10)]: 23676 +(test/StoreCore.t.sol) | set dynamic length of dynamic index 1 [StoreCoreInternal._setDynamicDataLengthAtIndex(table, key, 1, 99)]: 1777 +(test/StoreCore.t.sol) | reduce dynamic length of dynamic index 0 [StoreCoreInternal._setDynamicDataLengthAtIndex(table, key, 0, 5)]: 1768 (test/StoreCore.t.sol) | set static field (1 slot) [StoreCore.setField(table, key, 0, abi.encodePacked(firstDataBytes))]: 38105 (test/StoreCore.t.sol) | get static field (1 slot) [bytes memory loadedData = StoreCore.getField(table, key, 0)]: 4592 (test/StoreCore.t.sol) | set static field (overlap 2 slot) [StoreCore.setField(table, key, 1, abi.encodePacked(secondDataBytes))]: 33895 (test/StoreCore.t.sol) | get static field (overlap 2 slot) [loadedData = StoreCore.getField(table, key, 1)]: 6375 -(test/StoreCore.t.sol) | set dynamic field (1 slot, first dynamic field) [StoreCore.setField(table, key, 2, thirdDataBytes)]: 57420 +(test/StoreCore.t.sol) | set dynamic field (1 slot, first dynamic field) [StoreCore.setField(table, key, 2, thirdDataBytes)]: 57377 (test/StoreCore.t.sol) | get dynamic field (1 slot, first dynamic field) [loadedData = StoreCore.getField(table, key, 2)]: 5357 -(test/StoreCore.t.sol) | set dynamic field (1 slot, second dynamic field) [StoreCore.setField(table, key, 3, fourthDataBytes)]: 35554 +(test/StoreCore.t.sol) | set dynamic field (1 slot, second dynamic field) [StoreCore.setField(table, key, 3, fourthDataBytes)]: 35511 (test/StoreCore.t.sol) | get dynamic field (1 slot, second dynamic field) [loadedData = StoreCore.getField(table, key, 3)]: 5372 (test/StoreCore.t.sol) | set static record (1 slot) [StoreCore.setRecord(table, key, data)]: 34136 (test/StoreCore.t.sol) | get static record (1 slot) [bytes memory loadedData = StoreCore.getRecord(table, key, schema)]: 4102 @@ -114,7 +105,7 @@ (test/StoreSwitch.t.sol) | check if delegatecall [isDelegate = StoreSwitch.isDelegateCall()]: 671 (test/StoreSwitch.t.sol) | check if delegatecall [isDelegate = StoreSwitch.isDelegateCall()]: 627 (test/System.t.sol) | extract msg.sender from calldata [address sender = _msgSender()]: 24 -(test/Vector2Table.t.sol) | register Vector2Table schema [Vector2Table.registerSchema()]: 28708 +(test/Vector2Table.t.sol) | register Vector2Table schema [Vector2Table.registerSchema()]: 28673 (test/Vector2Table.t.sol) | set Vector2Table record [Vector2Table.set({ key: key, x: 1, y: 2 })]: 35206 (test/Vector2Table.t.sol) | get Vector2Table record [Vector2 memory vector = Vector2Table.get(key)]: 6440 (test/World.t.sol) | call autonomous system via World contract [WorldWithWorldTestSystem(address(world)).WorldTestSystem_move(entity, 1, 2)]: 45511