@latticexyz/cli 1.41.0 → 2.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/{chunk-GR245KYP.js → chunk-J4DJQNIC.js} +679 -133
  2. package/dist/chunk-O57QENJ6.js +23039 -0
  3. package/dist/config/index.d.ts +606 -292
  4. package/dist/config/index.js +49 -26
  5. package/dist/index.d.ts +2 -110
  6. package/dist/index.js +9 -50
  7. package/dist/mud.js +937 -57
  8. package/dist/utils/index.d.ts +92 -4
  9. package/dist/utils/index.js +2 -3
  10. package/package.json +10 -9
  11. package/src/commands/deploy-v2.ts +11 -15
  12. package/src/commands/index.ts +2 -0
  13. package/src/commands/worldgen.ts +55 -0
  14. package/src/config/commonSchemas.ts +11 -13
  15. package/src/config/dynamicResolution.ts +49 -0
  16. package/src/config/index.ts +15 -3
  17. package/src/config/loadStoreConfig.ts +1 -1
  18. package/src/config/parseStoreConfig.test-d.ts +31 -5
  19. package/src/config/parseStoreConfig.ts +218 -78
  20. package/src/config/validation.ts +25 -0
  21. package/src/config/world/index.ts +4 -0
  22. package/src/config/{loadWorldConfig.test-d.ts → world/loadWorldConfig.test-d.ts} +3 -3
  23. package/src/config/world/loadWorldConfig.ts +26 -0
  24. package/src/config/world/parseWorldConfig.ts +55 -0
  25. package/src/config/world/resolveWorldConfig.ts +80 -0
  26. package/src/config/world/userTypes.ts +72 -0
  27. package/src/index.ts +4 -6
  28. package/src/render-solidity/common.ts +51 -6
  29. package/src/render-solidity/field.ts +40 -44
  30. package/src/render-solidity/index.ts +5 -1
  31. package/src/render-solidity/record.ts +56 -73
  32. package/src/render-solidity/renderSystemInterface.ts +31 -0
  33. package/src/render-solidity/renderTable.ts +98 -70
  34. package/src/render-solidity/renderTypeHelpers.ts +99 -0
  35. package/src/render-solidity/renderTypesFromConfig.ts +2 -2
  36. package/src/render-solidity/renderWorld.ts +24 -0
  37. package/src/render-solidity/{renderTablesFromConfig.ts → tableOptions.ts} +28 -30
  38. package/src/render-solidity/tablegen.ts +20 -22
  39. package/src/render-solidity/types.ts +39 -5
  40. package/src/render-solidity/userType.ts +80 -48
  41. package/src/render-solidity/worldgen.ts +60 -0
  42. package/src/utils/contractToInterface.ts +130 -0
  43. package/src/utils/deploy-v2.ts +268 -101
  44. package/src/utils/formatAndWrite.ts +12 -0
  45. package/src/utils/getChainId.ts +10 -0
  46. package/src/utils/typeUtils.ts +17 -0
  47. package/dist/chunk-AER7UDD4.js +0 -0
  48. package/dist/chunk-XRS7KWBZ.js +0 -547
  49. package/dist/chunk-YZATC2M3.js +0 -397
  50. package/dist/chunk-ZYDMYSTH.js +0 -1178
  51. package/dist/deploy-v2-b7b3207d.d.ts +0 -92
  52. package/src/config/loadWorldConfig.ts +0 -178
  53. package/src/constants.ts +0 -1
package/src/index.ts CHANGED
@@ -1,8 +1,7 @@
1
- export { parseStoreConfig } from "./config/parseStoreConfig.js";
2
1
  export { loadStoreConfig } from "./config/loadStoreConfig.js";
3
- export { loadWorldConfig, resolveWorldConfig, parseWorldConfig } from "./config/loadWorldConfig.js";
4
- export { renderTablesFromConfig } from "./render-solidity/renderTablesFromConfig.js";
5
- export { renderTable } from "./render-solidity/renderTable.js";
2
+ export { parseStoreConfig } from "./config/parseStoreConfig.js";
3
+ export { loadWorldConfig, resolveWorldConfig, parseWorldConfig } from "./config/world/index.js";
4
+ export { resolveTableId } from "./config/dynamicResolution.js";
6
5
 
7
6
  export type {
8
7
  StoreUserConfig,
@@ -13,5 +12,4 @@ export type {
13
12
  MUDConfig,
14
13
  } from "./config/index.js";
15
14
 
16
- export * from "./constants.js";
17
- export * from "./utils/index.js";
15
+ export { storeConfig, mudConfig } from "./config/index.js";
@@ -1,4 +1,5 @@
1
- import { ImportDatum, RenderTableOptions, RenderTableType } from "./types.js";
1
+ import path from "path";
2
+ import { ImportDatum, RenderTableOptions, RenderTableType, StaticResourceData } from "./types.js";
2
3
 
3
4
  export const renderedSolidityHeader = `// SPDX-License-Identifier: MIT
4
5
  pragma solidity >=0.8.0;
@@ -20,10 +21,13 @@ export function renderArguments(args: (string | undefined)[]) {
20
21
  return internalRenderList(",", filteredArgs, (arg) => arg);
21
22
  }
22
23
 
23
- export function renderCommonData({ staticRouteData, primaryKeys }: RenderTableOptions) {
24
- // static route means static tableId as well, and no tableId arguments
25
- const _tableId = staticRouteData ? "" : "_tableId";
26
- const _typedTableId = staticRouteData ? "" : "uint256 _tableId";
24
+ export function renderCommonData({
25
+ staticResourceData,
26
+ primaryKeys,
27
+ }: Pick<RenderTableOptions, "staticResourceData" | "primaryKeys">) {
28
+ // static resource means static tableId as well, and no tableId arguments
29
+ const _tableId = staticResourceData ? "" : "_tableId";
30
+ const _typedTableId = staticResourceData ? "" : "uint256 _tableId";
27
31
 
28
32
  const _keyArgs = renderArguments(primaryKeys.map(({ name }) => name));
29
33
  const _typedKeyArgs = renderArguments(primaryKeys.map(({ name, typeWithLocation }) => `${typeWithLocation} ${name}`));
@@ -45,6 +49,14 @@ export function renderCommonData({ staticRouteData, primaryKeys }: RenderTableOp
45
49
  };
46
50
  }
47
51
 
52
+ /** For 2 paths which are relative to a common root, create a relative import path from one to another */
53
+ export function solidityRelativeImportPath(fromPath: string, usedInPath: string) {
54
+ // 1st "./" must be added because path strips it,
55
+ // but solidity expects it unless there's "../" ("./../" is fine).
56
+ // 2nd and 3rd "./" forcefully avoid absolute paths (everything is relative to `src`).
57
+ return "./" + path.relative("./" + usedInPath, "./" + fromPath);
58
+ }
59
+
48
60
  /**
49
61
  * Aggregates, deduplicates and renders imports for symbols per path.
50
62
  * Identical symbols from different paths are NOT handled, they should be checked before rendering.
@@ -52,7 +64,8 @@ export function renderCommonData({ staticRouteData, primaryKeys }: RenderTableOp
52
64
  export function renderImports(imports: ImportDatum[]) {
53
65
  // Aggregate symbols by import path, also deduplicating them
54
66
  const aggregatedImports = new Map<string, Set<string>>();
55
- for (const { symbol, path } of imports) {
67
+ for (const { symbol, fromPath, usedInPath } of imports) {
68
+ const path = solidityRelativeImportPath(fromPath, usedInPath);
56
69
  if (!aggregatedImports.has(path)) {
57
70
  aggregatedImports.set(path, new Set());
58
71
  }
@@ -67,6 +80,38 @@ export function renderImports(imports: ImportDatum[]) {
67
80
  return renderedImports.join("\n");
68
81
  }
69
82
 
83
+ export function renderWithStore(
84
+ storeArgument: boolean,
85
+ callback: (
86
+ _typedStore: string | undefined,
87
+ _store: string,
88
+ _commentSuffix: string,
89
+ _untypedStore: string | undefined
90
+ ) => string
91
+ ) {
92
+ let result = "";
93
+ result += callback(undefined, "StoreSwitch", "", undefined);
94
+
95
+ if (storeArgument) {
96
+ result += "\n" + callback("IStore _store", "_store", " (using the specified store)", "_store");
97
+ }
98
+
99
+ return result;
100
+ }
101
+
102
+ export function renderTableId(staticResourceData: StaticResourceData) {
103
+ const hardcodedTableId = `uint256(bytes32(abi.encodePacked(bytes16("${staticResourceData.namespace}"), bytes16("${staticResourceData.fileSelector}"))))`;
104
+
105
+ const tableIdDefinition = `
106
+ uint256 constant _tableId = ${hardcodedTableId};
107
+ uint256 constant ${staticResourceData.tableIdName} = _tableId;
108
+ `;
109
+ return {
110
+ hardcodedTableId,
111
+ tableIdDefinition,
112
+ };
113
+ }
114
+
70
115
  function renderValueTypeToBytes32(name: string, { staticByteLength, typeUnwrap, internalTypeId }: RenderTableType) {
71
116
  const bits = staticByteLength * 8;
72
117
  const innerText = `${typeUnwrap}(${name})`;
@@ -1,70 +1,66 @@
1
- import { renderArguments, renderCommonData } from "./common.js";
1
+ import { renderArguments, renderCommonData, renderWithStore } from "./common.js";
2
2
  import { RenderTableField, RenderTableOptions, RenderTableType } from "./types.js";
3
3
 
4
4
  export function renderFieldMethods(options: RenderTableOptions) {
5
+ const storeArgument = options.storeArgument;
5
6
  const { _typedTableId, _typedKeyArgs, _primaryKeysDefinition } = renderCommonData(options);
6
7
 
7
8
  let result = "";
8
9
  for (const [index, field] of options.fields.entries()) {
9
10
  const _typedFieldName = `${field.typeWithLocation} ${field.name}`;
10
11
 
11
- result += `
12
- /** Get ${field.name} */
13
- function get${field.methodNameSuffix}(${renderArguments([
14
- _typedTableId,
15
- _typedKeyArgs,
16
- ])}) internal view returns (${_typedFieldName}) {
17
- ${_primaryKeysDefinition}
18
- bytes memory _blob = StoreSwitch.getField(_tableId, _primaryKeys, ${index});
19
- return ${renderDecodeFieldSingle(field)};
20
- }
21
- `;
22
-
23
- if (options.storeArgument) {
24
- result += `
25
- /** Get ${field.name} from the specified store */
12
+ result += renderWithStore(
13
+ storeArgument,
14
+ (_typedStore, _store, _commentSuffix) => `
15
+ /** Get ${field.name}${_commentSuffix} */
26
16
  function get${field.methodNameSuffix}(${renderArguments([
17
+ _typedStore,
27
18
  _typedTableId,
28
- `IStore _store`,
29
19
  _typedKeyArgs,
30
20
  ])}) internal view returns (${_typedFieldName}) {
31
21
  ${_primaryKeysDefinition}
32
- bytes memory _blob = _store.getField(_tableId, _primaryKeys, ${index});
22
+ bytes memory _blob = ${_store}.getField(_tableId, _primaryKeys, ${index});
33
23
  return ${renderDecodeFieldSingle(field)};
34
24
  }
35
- `;
36
- }
25
+ `
26
+ );
37
27
 
38
- result += `
39
- /** Set ${field.name} */
40
- function set${field.methodNameSuffix}(${renderArguments([
41
- _typedTableId,
42
- _typedKeyArgs,
43
- _typedFieldName,
44
- ])}) internal {
45
- ${_primaryKeysDefinition}
46
- StoreSwitch.setField(_tableId, _primaryKeys, ${index}, ${renderEncodeField(field)});
47
- }
48
- `;
28
+ result += renderWithStore(
29
+ storeArgument,
30
+ (_typedStore, _store, _commentSuffix) => `
31
+ /** Set ${field.name}${_commentSuffix} */
32
+ function set${field.methodNameSuffix}(${renderArguments([
33
+ _typedStore,
34
+ _typedTableId,
35
+ _typedKeyArgs,
36
+ _typedFieldName,
37
+ ])}) internal {
38
+ ${_primaryKeysDefinition}
39
+ ${_store}.setField(_tableId, _primaryKeys, ${index}, ${renderEncodeField(field)});
40
+ }
41
+ `
42
+ );
49
43
 
50
44
  // 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
51
45
  // (see https://github.com/latticexyz/mud/issues/438)
52
46
  if (field.isDynamic) {
53
47
  const portionData = fieldPortionData(field);
54
48
 
55
- result += `
56
- /** Push ${portionData.title} to ${field.name} */
57
- function push${field.methodNameSuffix}(${renderArguments([
58
- _typedTableId,
59
- _typedKeyArgs,
60
- `${portionData.typeWithLocation} ${portionData.name}`,
61
- ])}) internal {
62
- ${_primaryKeysDefinition}
63
- bytes memory _blob = StoreSwitch.getField(_tableId, _primaryKeys, ${index});
64
- bytes memory _newBlob = abi.encodePacked(_blob, ${portionData.encoded});
65
- StoreSwitch.setField(_tableId, _primaryKeys, ${index}, _newBlob);
66
- }
67
- `;
49
+ result += renderWithStore(
50
+ storeArgument,
51
+ (_typedStore, _store, _commentSuffix) => `
52
+ /** Push ${portionData.title} to ${field.name}${_commentSuffix} */
53
+ function push${field.methodNameSuffix}(${renderArguments([
54
+ _typedStore,
55
+ _typedTableId,
56
+ _typedKeyArgs,
57
+ `${portionData.typeWithLocation} ${portionData.name}`,
58
+ ])}) internal {
59
+ ${_primaryKeysDefinition}
60
+ ${_store}.pushToField(_tableId, _primaryKeys, ${index}, ${portionData.encoded});
61
+ }
62
+ `
63
+ );
68
64
  }
69
65
  }
70
66
  return result;
@@ -2,5 +2,9 @@ export * from "./common.js";
2
2
  export * from "./field.js";
3
3
  export * from "./record.js";
4
4
  export * from "./renderTable.js";
5
- export * from "./renderTablesFromConfig.js";
5
+ export * from "./renderTypes.js";
6
+ export * from "./renderTypesFromConfig.js";
7
+ export * from "./tablegen.js";
8
+ export * from "./tableOptions.js";
6
9
  export * from "./types.js";
10
+ export * from "./userType.js";
@@ -1,71 +1,66 @@
1
- import { renderList, renderArguments, renderCommonData } from "./common.js";
2
- import { renderDecodeValueType, renderEncodeField } from "./field.js";
1
+ import { renderList, renderArguments, renderCommonData, renderWithStore } from "./common.js";
2
+ import { renderDecodeValueType } from "./field.js";
3
3
  import { RenderTableDynamicField, RenderTableOptions } from "./types.js";
4
4
 
5
5
  export function renderRecordMethods(options: RenderTableOptions) {
6
- const { staticFields, dynamicFields, structName, storeArgument } = options;
6
+ const { structName, storeArgument } = options;
7
7
  const { _tableId, _typedTableId, _keyArgs, _typedKeyArgs, _primaryKeysDefinition } = renderCommonData(options);
8
8
 
9
- let result = `
10
- /** Get the full data */
11
- function get(${renderArguments([_typedTableId, _typedKeyArgs])}) internal view returns (${renderDecodedRecord(
12
- options
13
- )}) {
14
- ${_primaryKeysDefinition}
15
- bytes memory _blob = StoreSwitch.getRecord(_tableId, _primaryKeys, getSchema());
16
- return decode(_blob);
17
- }
18
- `;
19
-
20
- if (storeArgument) {
21
- result += `
22
- /** Get the full data from the specified store */
9
+ let result = renderWithStore(
10
+ storeArgument,
11
+ (_typedStore, _store, _commentSuffix) => `
12
+ /** Get the full data${_commentSuffix} */
23
13
  function get(${renderArguments([
14
+ _typedStore,
24
15
  _typedTableId,
25
- `IStore _store`,
26
16
  _typedKeyArgs,
27
17
  ])}) internal view returns (${renderDecodedRecord(options)}) {
28
18
  ${_primaryKeysDefinition}
29
- bytes memory _blob = _store.getRecord(_tableId, _primaryKeys);
19
+ bytes memory _blob = ${_store}.getRecord(_tableId, _primaryKeys, getSchema());
30
20
  return decode(_blob);
31
21
  }
32
- `;
33
- }
34
-
35
- result += `
36
- /** Set the full data using individual values */
37
- function set(${renderArguments([
38
- _typedTableId,
39
- _typedKeyArgs,
40
- renderArguments(options.fields.map(({ name, typeWithLocation }) => `${typeWithLocation} ${name}`)),
41
- ])}) internal {
42
- ${renderEncodedLengths(dynamicFields)}
43
- bytes memory _data = abi.encodePacked(${renderArguments([
44
- renderArguments(staticFields.map(({ name }) => name)),
45
- // TODO try gas optimization (preallocate for all, encodePacked statics, and direct encode dynamics)
46
- // (see https://github.com/latticexyz/mud/issues/444)
47
- ...(dynamicFields.length === 0
48
- ? []
49
- : ["_encodedLengths.unwrap()", renderArguments(dynamicFields.map((field) => renderEncodeField(field)))]),
50
- ])});
22
+ `
23
+ );
24
+
25
+ result += renderWithStore(
26
+ storeArgument,
27
+ (_typedStore, _store, _commentSuffix) => `
28
+ /** Set the full data using individual values${_commentSuffix} */
29
+ function set(${renderArguments([
30
+ _typedStore,
31
+ _typedTableId,
32
+ _typedKeyArgs,
33
+ renderArguments(options.fields.map(({ name, typeWithLocation }) => `${typeWithLocation} ${name}`)),
34
+ ])}) internal {
35
+ bytes memory _data = encode(${renderArguments(options.fields.map(({ name }) => name))});
51
36
 
52
- ${_primaryKeysDefinition}
37
+ ${_primaryKeysDefinition}
53
38
 
54
- StoreSwitch.setRecord(_tableId, _primaryKeys, _data);
55
- }
56
- `;
39
+ ${_store}.setRecord(_tableId, _primaryKeys, _data);
40
+ }
41
+ `
42
+ );
57
43
 
58
44
  if (structName !== undefined) {
59
- result += `
60
- /** Set the full data using the data struct */
61
- function set(${renderArguments([_typedTableId, _typedKeyArgs, `${structName} memory _table`])}) internal {
62
- set(${renderArguments([
63
- _tableId,
64
- _keyArgs,
65
- renderArguments(options.fields.map(({ name }) => `_table.${name}`)),
66
- ])});
67
- }
68
- `;
45
+ result += renderWithStore(
46
+ storeArgument,
47
+ (_typedStore, _store, _commentSuffix, _untypedStore) => `
48
+ /** Set the full data using the data struct${_commentSuffix} */
49
+ function set(${renderArguments([
50
+ _typedStore,
51
+ _typedTableId,
52
+ _typedKeyArgs,
53
+ `${structName} memory _table`,
54
+ ])}) internal {
55
+ set(${renderArguments([
56
+ _untypedStore,
57
+ _tableId,
58
+ _keyArgs,
59
+ renderArguments(options.fields.map(({ name }) => `_table.${name}`)),
60
+ ])});
61
+ }
62
+ `
63
+ );
69
64
  }
70
65
 
71
66
  result += renderDecodeFunction(options);
@@ -142,30 +137,18 @@ function renderDecodedRecord({ structName, fields }: RenderTableOptions) {
142
137
  }
143
138
 
144
139
  function renderDecodeDynamicFieldPartial(field: RenderTableDynamicField) {
145
- const { typeId, arrayElement } = field;
140
+ const { typeId, arrayElement, typeWrap } = field;
146
141
  if (arrayElement) {
147
142
  // arrays
148
- return `SliceLib.getSubslice(_blob, _start, _end).decodeArray_${arrayElement.typeId}()`;
143
+ return `${typeWrap}(
144
+ SliceLib.getSubslice(_blob, _start, _end).decodeArray_${arrayElement.typeId}()
145
+ )`;
149
146
  } else {
150
147
  // bytes/string
151
- return `${typeId}(SliceLib.getSubslice(_blob, _start, _end).toBytes())`;
152
- }
153
- }
154
-
155
- function renderEncodedLengths(dynamicFields: RenderTableDynamicField[]) {
156
- if (dynamicFields.length > 0) {
157
- return `
158
- uint16[] memory _counters = new uint16[](${dynamicFields.length});
159
- ${renderList(dynamicFields, ({ name, arrayElement }, index) => {
160
- if (arrayElement) {
161
- return `_counters[${index}] = uint16(${name}.length * ${arrayElement.staticByteLength});`;
162
- } else {
163
- return `_counters[${index}] = uint16(bytes(${name}).length);`;
164
- }
165
- })}
166
- PackedCounter _encodedLengths = PackedCounterLib.pack(_counters);
167
- `;
168
- } else {
169
- return "";
148
+ return `${typeWrap}(
149
+ ${typeId}(
150
+ SliceLib.getSubslice(_blob, _start, _end).toBytes()
151
+ )
152
+ )`;
170
153
  }
171
154
  }
@@ -0,0 +1,31 @@
1
+ import { renderArguments, renderList, renderedSolidityHeader, renderImports } from "./common.js";
2
+ import { RenderSystemInterfaceOptions } from "./types.js";
3
+
4
+ export function renderSystemInterface(options: RenderSystemInterfaceOptions) {
5
+ const { imports, name, functionPrefix, functions } = options;
6
+
7
+ return `${renderedSolidityHeader}
8
+
9
+ ${renderImports(imports)}
10
+
11
+ interface ${name} {
12
+ ${renderList(
13
+ functions,
14
+ ({ name, parameters, returnParameters }) => `
15
+ function ${functionPrefix}${name}(${renderArguments(parameters)}) external ${renderReturnParameters(
16
+ returnParameters
17
+ )};
18
+ `
19
+ )}
20
+ }
21
+
22
+ `;
23
+ }
24
+
25
+ function renderReturnParameters(returnParameters: string[]) {
26
+ if (returnParameters.length > 0) {
27
+ return `returns (${renderArguments(returnParameters)})`;
28
+ } else {
29
+ return "";
30
+ }
31
+ }
@@ -1,10 +1,31 @@
1
- import { renderArguments, renderCommonData, renderList, renderedSolidityHeader, renderImports } from "./common.js";
2
- import { renderFieldMethods } from "./field.js";
1
+ import {
2
+ renderArguments,
3
+ renderCommonData,
4
+ renderList,
5
+ renderedSolidityHeader,
6
+ renderImports,
7
+ renderTableId,
8
+ renderWithStore,
9
+ } from "./common.js";
10
+ import { renderEncodeField, renderFieldMethods } from "./field.js";
3
11
  import { renderRecordMethods } from "./record.js";
4
- import { RenderTableOptions } from "./types.js";
12
+ import { renderTypeHelpers } from "./renderTypeHelpers.js";
13
+ import { RenderTableDynamicField, RenderTableOptions } from "./types.js";
5
14
 
6
15
  export function renderTable(options: RenderTableOptions) {
7
- const { imports, libraryName, structName, staticRouteData, storeImportPath, fields, withRecordMethods } = options;
16
+ const {
17
+ imports,
18
+ libraryName,
19
+ structName,
20
+ staticResourceData,
21
+ storeImportPath,
22
+ fields,
23
+ staticFields,
24
+ dynamicFields,
25
+ withRecordMethods,
26
+ storeArgument,
27
+ primaryKeys,
28
+ } = options;
8
29
 
9
30
  const { _typedTableId, _typedKeyArgs, _primaryKeysDefinition } = renderCommonData(options);
10
31
 
@@ -18,6 +39,7 @@ import { IStore } from "${storeImportPath}IStore.sol";
18
39
  import { StoreSwitch } from "${storeImportPath}StoreSwitch.sol";
19
40
  import { StoreCore } from "${storeImportPath}StoreCore.sol";
20
41
  import { Bytes } from "${storeImportPath}Bytes.sol";
42
+ import { Memory } from "${storeImportPath}Memory.sol";
21
43
  import { SliceLib } from "${storeImportPath}Slice.sol";
22
44
  import { EncodeArray } from "${storeImportPath}tightcoder/EncodeArray.sol";
23
45
  import { Schema, SchemaLib } from "${storeImportPath}Schema.sol";
@@ -32,14 +54,7 @@ ${
32
54
  : ""
33
55
  }
34
56
 
35
- ${
36
- !staticRouteData
37
- ? ""
38
- : `
39
- uint256 constant _tableId = uint256(keccak256("${staticRouteData.baseRoute + staticRouteData.subRoute}"));
40
- uint256 constant ${staticRouteData.tableIdName} = _tableId;
41
- `
42
- }
57
+ ${staticResourceData ? renderTableId(staticResourceData).tableIdDefinition : ""}
43
58
 
44
59
  ${
45
60
  !structName
@@ -60,6 +75,13 @@ library ${libraryName} {
60
75
  return SchemaLib.encode(_schema);
61
76
  }
62
77
 
78
+ function getKeySchema() internal pure returns (Schema) {
79
+ SchemaType[] memory _schema = new SchemaType[](${primaryKeys.length});
80
+ ${renderList(primaryKeys, ({ enumName }, index) => `_schema[${index}] = SchemaType.${enumName};`)}
81
+
82
+ return SchemaLib.encode(_schema);
83
+ }
84
+
63
85
  /** Get the table's metadata */
64
86
  function getMetadata() internal pure returns (string memory, string[] memory) {
65
87
  string[] memory _fieldNames = new string[](${fields.length});
@@ -67,70 +89,76 @@ library ${libraryName} {
67
89
  return ("${libraryName}", _fieldNames);
68
90
  }
69
91
 
70
- /** Register the table's schema */
71
- function registerSchema(${_typedTableId}) internal {
72
- StoreSwitch.registerSchema(_tableId, getSchema());
73
- }
74
-
75
- /** Set the table's metadata */
76
- function setMetadata(${_typedTableId}) internal {
77
- (string memory _tableName, string[] memory _fieldNames) = getMetadata();
78
- StoreSwitch.setMetadata(_tableId, _tableName, _fieldNames);
79
- }
80
-
81
- ${
82
- !options.storeArgument
83
- ? ""
84
- : `
85
- /** Register the table's schema for the specified store */
86
- function registerSchema(${renderArguments([_typedTableId, "IStore _store"])}) internal {
87
- _store.registerSchema(_tableId, getSchema());
92
+ ${renderWithStore(
93
+ storeArgument,
94
+ (_typedStore, _store, _commentSuffix) => `
95
+ /** Register the table's schema${_commentSuffix} */
96
+ function registerSchema(${renderArguments([_typedStore, _typedTableId])}) internal {
97
+ ${_store}.registerSchema(_tableId, getSchema(), getKeySchema());
98
+ }
99
+ `
100
+ )}
101
+ ${renderWithStore(
102
+ storeArgument,
103
+ (_typedStore, _store, _commentSuffix) => `
104
+ /** Set the table's metadata${_commentSuffix} */
105
+ function setMetadata(${renderArguments([_typedStore, _typedTableId])}) internal {
106
+ (string memory _tableName, string[] memory _fieldNames) = getMetadata();
107
+ ${_store}.setMetadata(_tableId, _tableName, _fieldNames);
108
+ }
109
+ `
110
+ )}
111
+
112
+ ${renderFieldMethods(options)}
113
+
114
+ ${withRecordMethods ? renderRecordMethods(options) : ""}
115
+
116
+ /** Tightly pack full data using this table's schema */
117
+ function encode(${renderArguments(
118
+ options.fields.map(({ name, typeWithLocation }) => `${typeWithLocation} ${name}`)
119
+ )}) internal view returns (bytes memory) {
120
+ ${renderEncodedLengths(dynamicFields)}
121
+ return abi.encodePacked(${renderArguments([
122
+ renderArguments(staticFields.map(({ name }) => name)),
123
+ // TODO try gas optimization (preallocate for all, encodePacked statics, and direct encode dynamics)
124
+ // (see https://github.com/latticexyz/mud/issues/444)
125
+ ...(dynamicFields.length === 0
126
+ ? []
127
+ : ["_encodedLengths.unwrap()", renderArguments(dynamicFields.map((field) => renderEncodeField(field)))]),
128
+ ])});
88
129
  }
89
130
 
90
- /** Set the table's metadata for the specified store */
91
- function setMetadata(${renderArguments([_typedTableId, "IStore _store"])}) internal {
92
- (string memory _tableName, string[] memory _fieldNames) = getMetadata();
93
- _store.setMetadata(_tableId, _tableName, _fieldNames);
94
- }
95
- `
131
+ ${renderWithStore(
132
+ storeArgument,
133
+ (_typedStore, _store, _commentSuffix) => `
134
+ /* Delete all data for given keys${_commentSuffix} */
135
+ function deleteRecord(${renderArguments([_typedStore, _typedTableId, _typedKeyArgs])}) internal {
136
+ ${_primaryKeysDefinition}
137
+ ${_store}.deleteRecord(_tableId, _primaryKeys);
138
+ }
139
+ `
140
+ )}
96
141
  }
97
142
 
98
- ${renderFieldMethods(options)}
143
+ ${renderTypeHelpers(options)}
99
144
 
100
- ${withRecordMethods ? renderRecordMethods(options) : ""}
101
-
102
- /* Delete all data for given keys */
103
- function deleteRecord(${renderArguments([_typedTableId, _typedKeyArgs])}) internal {
104
- ${_primaryKeysDefinition}
105
- StoreSwitch.deleteRecord(_tableId, _primaryKeys);
106
- }
107
- }
108
-
109
- ${
110
- // nothing can be cast to bool, so an assembly helper is required
111
- !fields.some(({ typeId }) => typeId === "bool")
112
- ? ""
113
- : `
114
- function _toBool(uint8 value) pure returns (bool result) {
115
- assembly {
116
- result := value
117
- }
118
- }
119
- `
145
+ `;
120
146
  }
121
147
 
122
- ${
123
- // nothing can be cast from bool, so an assembly helper is required
124
- !options.primaryKeys.some(({ typeId }) => typeId === "bool")
125
- ? ""
126
- : `
127
- function _boolToBytes32(bool value) pure returns (bytes32 result) {
128
- assembly {
129
- result := value
130
- }
148
+ function renderEncodedLengths(dynamicFields: RenderTableDynamicField[]) {
149
+ if (dynamicFields.length > 0) {
150
+ return `
151
+ uint16[] memory _counters = new uint16[](${dynamicFields.length});
152
+ ${renderList(dynamicFields, ({ name, arrayElement }, index) => {
153
+ if (arrayElement) {
154
+ return `_counters[${index}] = uint16(${name}.length * ${arrayElement.staticByteLength});`;
155
+ } else {
156
+ return `_counters[${index}] = uint16(bytes(${name}).length);`;
157
+ }
158
+ })}
159
+ PackedCounter _encodedLengths = PackedCounterLib.pack(_counters);
160
+ `;
161
+ } else {
162
+ return "";
131
163
  }
132
- `
133
- }
134
-
135
- `;
136
164
  }