@latticexyz/common 2.0.0-next.15 → 2.0.0-next.17
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.
- package/dist/chunk-YEN6EYTC.js +2 -0
- package/dist/chunk-YEN6EYTC.js.map +1 -0
- package/dist/codegen.d.ts +110 -14
- package/dist/codegen.js +22 -31
- package/dist/codegen.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/codegen/render-solidity/common.test.ts +83 -0
- package/src/codegen/render-solidity/common.ts +105 -51
- package/src/codegen/render-solidity/renderEnums.ts +3 -0
- package/src/codegen/render-solidity/renderTypeHelpers.ts +19 -0
- package/src/codegen/render-solidity/types.ts +5 -0
- package/src/codegen/utils/contractToInterface.ts +59 -41
- package/src/codegen/utils/extractUserTypes.ts +7 -0
- package/src/codegen/utils/format.ts +11 -0
- package/src/codegen/utils/formatAndWrite.ts +12 -0
- package/src/codegen/utils/loadUserTypesFile.ts +8 -1
- package/dist/chunk-TCWGPC6G.js +0 -2
- package/dist/chunk-TCWGPC6G.js.map +0 -1
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { getLeftPaddingBits, isLeftAligned, renderTableId, renderValueTypeToBytes32 } from "./common";
|
|
3
|
+
|
|
4
|
+
describe("isLeftAligned", () => {
|
|
5
|
+
it("returns true when a Solidity type's bytes should be left aligned", () => {
|
|
6
|
+
expect(isLeftAligned({ internalTypeId: "bytes32" })).toBe(true);
|
|
7
|
+
expect(isLeftAligned({ internalTypeId: "uint256" })).toBe(false);
|
|
8
|
+
expect(isLeftAligned({ internalTypeId: "bool" })).toBe(false);
|
|
9
|
+
});
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
describe("getLeftPaddingBits", () => {
|
|
13
|
+
it("returns number of bits to left pad the bytes representation", () => {
|
|
14
|
+
expect(getLeftPaddingBits({ internalTypeId: "bytes32", staticByteLength: 32 })).toBe(0);
|
|
15
|
+
expect(getLeftPaddingBits({ internalTypeId: "bytes16", staticByteLength: 16 })).toBe(0);
|
|
16
|
+
expect(getLeftPaddingBits({ internalTypeId: "uint32", staticByteLength: 4 })).toBe(224);
|
|
17
|
+
expect(getLeftPaddingBits({ internalTypeId: "uint64", staticByteLength: 8 })).toBe(192);
|
|
18
|
+
expect(getLeftPaddingBits({ internalTypeId: "uint128", staticByteLength: 16 })).toBe(128);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
describe("renderValueTypeToBytes32", () => {
|
|
23
|
+
it("returns Solidity code to cast native type to bytes32", () => {
|
|
24
|
+
expect(renderValueTypeToBytes32("someField", { typeUnwrap: "", internalTypeId: "bytes32" })).toMatchInlineSnapshot(
|
|
25
|
+
'"someField"'
|
|
26
|
+
);
|
|
27
|
+
expect(
|
|
28
|
+
renderValueTypeToBytes32("someField", { typeUnwrap: "SomeStruct.unwrap", internalTypeId: "bytes32" })
|
|
29
|
+
).toMatchInlineSnapshot('"SomeStruct.unwrap(someField)"');
|
|
30
|
+
|
|
31
|
+
expect(renderValueTypeToBytes32("someField", { typeUnwrap: "", internalTypeId: "bytes16" })).toMatchInlineSnapshot(
|
|
32
|
+
'"bytes32(someField)"'
|
|
33
|
+
);
|
|
34
|
+
expect(
|
|
35
|
+
renderValueTypeToBytes32("someField", { typeUnwrap: "SomeStruct.unwrap", internalTypeId: "bytes16" })
|
|
36
|
+
).toMatchInlineSnapshot('"bytes32(SomeStruct.unwrap(someField))"');
|
|
37
|
+
|
|
38
|
+
expect(renderValueTypeToBytes32("someField", { typeUnwrap: "", internalTypeId: "uint8" })).toMatchInlineSnapshot(
|
|
39
|
+
'"bytes32(uint256(someField))"'
|
|
40
|
+
);
|
|
41
|
+
expect(
|
|
42
|
+
renderValueTypeToBytes32("someField", { typeUnwrap: "SomeStruct.unwrap", internalTypeId: "uint8" })
|
|
43
|
+
).toMatchInlineSnapshot('"bytes32(uint256(SomeStruct.unwrap(someField)))"');
|
|
44
|
+
|
|
45
|
+
expect(renderValueTypeToBytes32("someField", { typeUnwrap: "", internalTypeId: "bool" })).toMatchInlineSnapshot(
|
|
46
|
+
'"_boolToBytes32(someField)"'
|
|
47
|
+
);
|
|
48
|
+
expect(
|
|
49
|
+
renderValueTypeToBytes32("someField", { typeUnwrap: "SomeStruct.unwrap", internalTypeId: "bool" })
|
|
50
|
+
).toMatchInlineSnapshot('"_boolToBytes32(SomeStruct.unwrap(someField))"');
|
|
51
|
+
|
|
52
|
+
expect(renderValueTypeToBytes32("someField", { typeUnwrap: "", internalTypeId: "address" })).toMatchInlineSnapshot(
|
|
53
|
+
'"bytes32(uint256(uint160(someField)))"'
|
|
54
|
+
);
|
|
55
|
+
expect(
|
|
56
|
+
renderValueTypeToBytes32("someField", { typeUnwrap: "SomeStruct.unwrap", internalTypeId: "address" })
|
|
57
|
+
).toMatchInlineSnapshot('"bytes32(uint256(uint160(SomeStruct.unwrap(someField))))"');
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe("renderTableId", () => {
|
|
62
|
+
it("returns Solidity code to compute table ID", () => {
|
|
63
|
+
expect(renderTableId({ namespace: "somewhere", name: "Player", offchainOnly: false, tableIdName: "PlayerTableId" }))
|
|
64
|
+
.toMatchInlineSnapshot(`
|
|
65
|
+
"
|
|
66
|
+
// Hex below is the result of \`WorldResourceIdLib.encode({ namespace: \\"somewhere\\", name: \\"Player\\", typeId: RESOURCE_TABLE });\`
|
|
67
|
+
ResourceId constant _tableId = ResourceId.wrap(0x7462736f6d6577686572650000000000506c6179657200000000000000000000);
|
|
68
|
+
ResourceId constant PlayerTableId = _tableId;
|
|
69
|
+
"
|
|
70
|
+
`);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("returns Solidity code to compute offchain table ID", () => {
|
|
74
|
+
expect(renderTableId({ namespace: "somewhere", name: "Player", offchainOnly: true, tableIdName: "PlayerTableId" }))
|
|
75
|
+
.toMatchInlineSnapshot(`
|
|
76
|
+
"
|
|
77
|
+
// Hex below is the result of \`WorldResourceIdLib.encode({ namespace: \\"somewhere\\", name: \\"Player\\", typeId: RESOURCE_OFFCHAIN_TABLE });\`
|
|
78
|
+
ResourceId constant _tableId = ResourceId.wrap(0x6f74736f6d6577686572650000000000506c6179657200000000000000000000);
|
|
79
|
+
ResourceId constant PlayerTableId = _tableId;
|
|
80
|
+
"
|
|
81
|
+
`);
|
|
82
|
+
});
|
|
83
|
+
});
|
|
@@ -8,9 +8,13 @@ import {
|
|
|
8
8
|
RenderType,
|
|
9
9
|
} from "./types";
|
|
10
10
|
import { posixPath } from "../utils";
|
|
11
|
+
import { resourceToHex } from "../../resourceToHex";
|
|
11
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Common header for all codegenerated solidity files
|
|
15
|
+
*/
|
|
12
16
|
export const renderedSolidityHeader = `// SPDX-License-Identifier: MIT
|
|
13
|
-
pragma solidity >=0.8.
|
|
17
|
+
pragma solidity >=0.8.24;
|
|
14
18
|
|
|
15
19
|
/* Autogenerated file. Do not edit manually. */`;
|
|
16
20
|
|
|
@@ -29,24 +33,30 @@ export function renderArguments(args: (string | undefined)[]): string {
|
|
|
29
33
|
return internalRenderList(",", filteredArgs, (arg) => arg);
|
|
30
34
|
}
|
|
31
35
|
|
|
36
|
+
interface RenderedCommonData {
|
|
37
|
+
/** `_tableId` variable prefixed with its type (empty string if absent) */
|
|
38
|
+
_typedTableId: string;
|
|
39
|
+
/** Comma-separated table key names prefixed with their types (empty string if 0 keys) */
|
|
40
|
+
_typedKeyArgs: string;
|
|
41
|
+
/** Definition and initialization of the dynamic `_keyTuple` bytes32 array */
|
|
42
|
+
_keyTupleDefinition: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Renders some solidity statements commonly used within table libraries
|
|
47
|
+
* @param param0.staticResourceData static data about the table library
|
|
48
|
+
* @param param0.keyTuple key tuple of the table library
|
|
49
|
+
* @returns Rendered statement strings
|
|
50
|
+
*/
|
|
32
51
|
export function renderCommonData({
|
|
33
52
|
staticResourceData,
|
|
34
53
|
keyTuple,
|
|
35
54
|
}: {
|
|
36
55
|
staticResourceData?: StaticResourceData;
|
|
37
56
|
keyTuple: RenderKeyTuple[];
|
|
38
|
-
}): {
|
|
39
|
-
_tableId: string;
|
|
40
|
-
_typedTableId: string;
|
|
41
|
-
_keyArgs: string;
|
|
42
|
-
_typedKeyArgs: string;
|
|
43
|
-
_keyTupleDefinition: string;
|
|
44
|
-
} {
|
|
57
|
+
}): RenderedCommonData {
|
|
45
58
|
// static resource means static tableId as well, and no tableId arguments
|
|
46
|
-
const _tableId = staticResourceData ? "" : "_tableId";
|
|
47
59
|
const _typedTableId = staticResourceData ? "" : "ResourceId _tableId";
|
|
48
|
-
|
|
49
|
-
const _keyArgs = renderArguments(keyTuple.map(({ name }) => name));
|
|
50
60
|
const _typedKeyArgs = renderArguments(keyTuple.map(({ name, typeWithLocation }) => `${typeWithLocation} ${name}`));
|
|
51
61
|
|
|
52
62
|
const _keyTupleDefinition = `
|
|
@@ -55,9 +65,7 @@ export function renderCommonData({
|
|
|
55
65
|
`;
|
|
56
66
|
|
|
57
67
|
return {
|
|
58
|
-
_tableId,
|
|
59
68
|
_typedTableId,
|
|
60
|
-
_keyArgs,
|
|
61
69
|
_typedKeyArgs,
|
|
62
70
|
_keyTupleDefinition,
|
|
63
71
|
};
|
|
@@ -125,28 +133,60 @@ export function renderAbsoluteImports(imports: AbsoluteImportDatum[]): string {
|
|
|
125
133
|
return renderedImports.join("\n");
|
|
126
134
|
}
|
|
127
135
|
|
|
136
|
+
interface RenderWithStoreCallbackData {
|
|
137
|
+
/** `_store` variable prefixed with its type (undefined if library name) */
|
|
138
|
+
_typedStore: string | undefined;
|
|
139
|
+
/** `_store` variable (undefined if library name) */
|
|
140
|
+
_store: string;
|
|
141
|
+
/** Empty string if storeArgument is false, otherwise `" (using the specified store)"` */
|
|
142
|
+
_commentSuffix: string;
|
|
143
|
+
/** Prefix to differentiate different kinds of store usage within methods */
|
|
144
|
+
_methodNamePrefix: string;
|
|
145
|
+
/** Whether FieldLayout variable should be passed to store methods */
|
|
146
|
+
_useExplicitFieldLayout?: boolean;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Renders several versions of the callback's result, which access Store in different ways
|
|
151
|
+
* @param storeArgument whether to render a version with `IStore _store` as an argument
|
|
152
|
+
* @param callback renderer for a method which uses store
|
|
153
|
+
* @returns Concatenated results of all callback calls
|
|
154
|
+
*/
|
|
128
155
|
export function renderWithStore(
|
|
129
156
|
storeArgument: boolean,
|
|
130
|
-
callback: (
|
|
131
|
-
_typedStore: string | undefined,
|
|
132
|
-
_store: string,
|
|
133
|
-
_commentSuffix: string,
|
|
134
|
-
_untypedStore: string | undefined,
|
|
135
|
-
_methodPrefix: string,
|
|
136
|
-
_internal?: boolean
|
|
137
|
-
) => string
|
|
157
|
+
callback: (data: RenderWithStoreCallbackData) => string
|
|
138
158
|
): string {
|
|
139
159
|
let result = "";
|
|
140
|
-
result += callback(undefined, "StoreSwitch", "",
|
|
141
|
-
result += callback(
|
|
160
|
+
result += callback({ _typedStore: undefined, _store: "StoreSwitch", _commentSuffix: "", _methodNamePrefix: "" });
|
|
161
|
+
result += callback({
|
|
162
|
+
_typedStore: undefined,
|
|
163
|
+
_store: "StoreCore",
|
|
164
|
+
_commentSuffix: "",
|
|
165
|
+
_methodNamePrefix: "_",
|
|
166
|
+
_useExplicitFieldLayout: true,
|
|
167
|
+
});
|
|
142
168
|
|
|
143
169
|
if (storeArgument) {
|
|
144
|
-
result +=
|
|
170
|
+
result +=
|
|
171
|
+
"\n" +
|
|
172
|
+
callback({
|
|
173
|
+
_typedStore: "IStore _store",
|
|
174
|
+
_store: "_store",
|
|
175
|
+
_commentSuffix: " (using the specified store)",
|
|
176
|
+
_methodNamePrefix: "",
|
|
177
|
+
});
|
|
145
178
|
}
|
|
146
179
|
|
|
147
180
|
return result;
|
|
148
181
|
}
|
|
149
182
|
|
|
183
|
+
/**
|
|
184
|
+
* Renders several versions of the callback's result, which have different method name suffixes
|
|
185
|
+
* @param withSuffixlessFieldMethods whether to render methods with an empty suffix
|
|
186
|
+
* @param fieldName name of the field which the methods access, used for a suffix
|
|
187
|
+
* @param callback renderer for a method to be suffixed
|
|
188
|
+
* @returns Concatenated results of all callback calls
|
|
189
|
+
*/
|
|
150
190
|
export function renderWithFieldSuffix(
|
|
151
191
|
withSuffixlessFieldMethods: boolean,
|
|
152
192
|
fieldName: string,
|
|
@@ -163,42 +203,47 @@ export function renderWithFieldSuffix(
|
|
|
163
203
|
return result;
|
|
164
204
|
}
|
|
165
205
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
)
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
206
|
+
/**
|
|
207
|
+
* Renders `_tableId` variable definition and initialization, and its alias which uses the provided `tableIdName`.
|
|
208
|
+
* @param param0 static resource data needed to construct the table id
|
|
209
|
+
*/
|
|
210
|
+
export function renderTableId({
|
|
211
|
+
namespace,
|
|
212
|
+
name,
|
|
213
|
+
offchainOnly,
|
|
214
|
+
tableIdName,
|
|
215
|
+
}: Pick<StaticResourceData, "namespace" | "name" | "offchainOnly" | "tableIdName">): string {
|
|
216
|
+
return `
|
|
217
|
+
// Hex below is the result of \`WorldResourceIdLib.encode({ namespace: ${JSON.stringify(
|
|
218
|
+
namespace
|
|
219
|
+
)}, name: ${JSON.stringify(name)}, typeId: ${offchainOnly ? "RESOURCE_OFFCHAIN_TABLE" : "RESOURCE_TABLE"} });\`
|
|
220
|
+
ResourceId constant _tableId = ResourceId.wrap(${resourceToHex({
|
|
221
|
+
type: offchainOnly ? "offchainTable" : "table",
|
|
222
|
+
namespace,
|
|
223
|
+
name,
|
|
224
|
+
})});
|
|
184
225
|
ResourceId constant ${tableIdName} = _tableId;
|
|
185
226
|
`;
|
|
186
|
-
return {
|
|
187
|
-
hardcodedTableId,
|
|
188
|
-
tableIdDefinition,
|
|
189
|
-
};
|
|
190
227
|
}
|
|
191
228
|
|
|
192
|
-
|
|
229
|
+
/**
|
|
230
|
+
* Renders solidity typecasts to get from the given type to `bytes32`
|
|
231
|
+
* @param name variable name to be typecasted
|
|
232
|
+
* @param param1 type data
|
|
233
|
+
*/
|
|
234
|
+
export function renderValueTypeToBytes32(
|
|
235
|
+
name: string,
|
|
236
|
+
{ typeUnwrap, internalTypeId }: Pick<RenderType, "typeUnwrap" | "internalTypeId">
|
|
237
|
+
): string {
|
|
193
238
|
const innerText = typeUnwrap.length ? `${typeUnwrap}(${name})` : name;
|
|
194
239
|
|
|
195
240
|
if (internalTypeId === "bytes32") {
|
|
196
241
|
return innerText;
|
|
197
|
-
} else if (
|
|
242
|
+
} else if (/^bytes\d{1,2}$/.test(internalTypeId)) {
|
|
198
243
|
return `bytes32(${innerText})`;
|
|
199
|
-
} else if (
|
|
244
|
+
} else if (/^uint\d{1,3}$/.test(internalTypeId)) {
|
|
200
245
|
return `bytes32(uint256(${innerText}))`;
|
|
201
|
-
} else if (
|
|
246
|
+
} else if (/^int\d{1,3}$/.test(internalTypeId)) {
|
|
202
247
|
return `bytes32(uint256(int256(${innerText})))`;
|
|
203
248
|
} else if (internalTypeId === "address") {
|
|
204
249
|
return `bytes32(uint256(uint160(${innerText})))`;
|
|
@@ -209,10 +254,16 @@ export function renderValueTypeToBytes32(name: string, { typeUnwrap, internalTyp
|
|
|
209
254
|
}
|
|
210
255
|
}
|
|
211
256
|
|
|
257
|
+
/**
|
|
258
|
+
* Whether the storage representation of the given solidity type is left aligned
|
|
259
|
+
*/
|
|
212
260
|
export function isLeftAligned(field: Pick<RenderType, "internalTypeId">): boolean {
|
|
213
|
-
return
|
|
261
|
+
return /^bytes\d{1,2}$/.test(field.internalTypeId);
|
|
214
262
|
}
|
|
215
263
|
|
|
264
|
+
/**
|
|
265
|
+
* The number of padding bits in the storage representation of a right-aligned solidity type
|
|
266
|
+
*/
|
|
216
267
|
export function getLeftPaddingBits(field: Pick<RenderType, "internalTypeId" | "staticByteLength">): number {
|
|
217
268
|
if (isLeftAligned(field)) {
|
|
218
269
|
return 0;
|
|
@@ -221,6 +272,9 @@ export function getLeftPaddingBits(field: Pick<RenderType, "internalTypeId" | "s
|
|
|
221
272
|
}
|
|
222
273
|
}
|
|
223
274
|
|
|
275
|
+
/**
|
|
276
|
+
* Internal helper to render `lineTerminator`-separated list of items mapped by `renderItem`
|
|
277
|
+
*/
|
|
224
278
|
function internalRenderList<T>(
|
|
225
279
|
lineTerminator: string,
|
|
226
280
|
list: T[],
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { renderArguments, renderList, renderedSolidityHeader } from "./common";
|
|
2
2
|
import { RenderEnum } from "./types";
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Render a list of enum data as solidity enum definitions
|
|
6
|
+
*/
|
|
4
7
|
export function renderEnums(enums: RenderEnum[]): string {
|
|
5
8
|
let result = renderedSolidityHeader;
|
|
6
9
|
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { RenderField, RenderKeyTuple, RenderType } from "./types";
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Renders the necessary helper functions to typecast to/from the types of given fields and keys
|
|
5
|
+
*/
|
|
3
6
|
export function renderTypeHelpers(options: { fields: RenderField[]; keyTuple: RenderKeyTuple[] }): string {
|
|
4
7
|
const { fields, keyTuple } = options;
|
|
5
8
|
|
|
@@ -59,6 +62,14 @@ function getWrappingHelpers(array: RenderType[]): string[] {
|
|
|
59
62
|
return [...wrappers.values(), ...unwrappers.values()];
|
|
60
63
|
}
|
|
61
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Renders a function to cast a dynamic array to a static array.
|
|
67
|
+
* @param functionName name of the function to be rendered
|
|
68
|
+
* @param elementType type of the array's element
|
|
69
|
+
* @param staticLength length of the static array
|
|
70
|
+
* @param internalTypeId solidity type name of the dynamic array
|
|
71
|
+
* @returns
|
|
72
|
+
*/
|
|
62
73
|
function renderWrapperStaticArray(
|
|
63
74
|
functionName: string,
|
|
64
75
|
elementType: string,
|
|
@@ -96,6 +107,14 @@ function renderWrapperStaticArray(
|
|
|
96
107
|
`;
|
|
97
108
|
}
|
|
98
109
|
|
|
110
|
+
/**
|
|
111
|
+
* Renders a function to cast a static array to a dynamic array.
|
|
112
|
+
* @param functionName name of the function to be rendered
|
|
113
|
+
* @param elementType type of the array's element
|
|
114
|
+
* @param staticLength length of the static array
|
|
115
|
+
* @param internalTypeId solidity type name of the dynamic array
|
|
116
|
+
* @returns
|
|
117
|
+
*/
|
|
99
118
|
function renderUnwrapperStaticArray(
|
|
100
119
|
functionName: string,
|
|
101
120
|
elementType: string,
|
|
@@ -14,13 +14,18 @@ export type ImportDatum = AbsoluteImportDatum | RelativeImportDatum;
|
|
|
14
14
|
export interface StaticResourceData {
|
|
15
15
|
/** Name of the table id constant to render. */
|
|
16
16
|
tableIdName: string;
|
|
17
|
+
/** Table namespace string */
|
|
17
18
|
namespace: string;
|
|
19
|
+
/** Table name string */
|
|
18
20
|
name: string;
|
|
21
|
+
/** Whether the table is offchain only (does not use storage) */
|
|
19
22
|
offchainOnly: boolean;
|
|
20
23
|
}
|
|
21
24
|
|
|
22
25
|
export interface RenderType {
|
|
26
|
+
/** Fully-qualified name of the user-defined type (may include a library name as prefix) */
|
|
23
27
|
typeId: string;
|
|
28
|
+
/** Fully-qualified name of the user-defined type (may include a library name as prefix), followed by location (none/memory/storage) */
|
|
24
29
|
typeWithLocation: string;
|
|
25
30
|
/** The name of the enum element in SchemaType to use for schema registration (e.g. "UINT256_ARRAY") */
|
|
26
31
|
enumName: string;
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { parse, visit } from "@solidity-parser/parser";
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
ContractDefinition,
|
|
4
|
+
SourceUnit,
|
|
5
|
+
TypeName,
|
|
6
|
+
VariableDeclaration,
|
|
7
|
+
} from "@solidity-parser/parser/dist/src/ast-types";
|
|
3
8
|
import { MUDError } from "../../errors";
|
|
4
9
|
|
|
5
10
|
export interface ContractInterfaceFunction {
|
|
@@ -36,52 +41,55 @@ export function contractToInterface(
|
|
|
36
41
|
} {
|
|
37
42
|
const ast = parse(data);
|
|
38
43
|
|
|
39
|
-
|
|
44
|
+
const contractNode = findContractNode(parse(data), contractName);
|
|
40
45
|
let symbolImports: SymbolImport[] = [];
|
|
41
46
|
const functions: ContractInterfaceFunction[] = [];
|
|
42
47
|
const errors: ContractInterfaceError[] = [];
|
|
43
48
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
error.message = `Function "${name}" in contract "${contractName}": ${error.message}`;
|
|
49
|
+
if (!contractNode) {
|
|
50
|
+
throw new MUDError(`Contract not found: ${contractName}`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
visit(contractNode, {
|
|
54
|
+
FunctionDefinition({
|
|
55
|
+
name,
|
|
56
|
+
visibility,
|
|
57
|
+
parameters,
|
|
58
|
+
stateMutability,
|
|
59
|
+
returnParameters,
|
|
60
|
+
isConstructor,
|
|
61
|
+
isFallback,
|
|
62
|
+
isReceiveEther,
|
|
63
|
+
}) {
|
|
64
|
+
try {
|
|
65
|
+
// skip constructor and fallbacks
|
|
66
|
+
if (isConstructor || isFallback || isReceiveEther) return;
|
|
67
|
+
// forbid default visibility (this check might be unnecessary, modern solidity already disallows this)
|
|
68
|
+
if (visibility === "default") throw new MUDError(`Visibility is not specified`);
|
|
69
|
+
|
|
70
|
+
if (visibility === "external" || visibility === "public") {
|
|
71
|
+
functions.push({
|
|
72
|
+
name: name === null ? "" : name,
|
|
73
|
+
parameters: parameters.map(parseParameter),
|
|
74
|
+
stateMutability: stateMutability || "",
|
|
75
|
+
returnParameters: returnParameters === null ? [] : returnParameters.map(parseParameter),
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
for (const { typeName } of parameters.concat(returnParameters ?? [])) {
|
|
79
|
+
const symbols = typeNameToSymbols(typeName);
|
|
80
|
+
symbolImports = symbolImports.concat(symbolsToImports(ast, symbols));
|
|
77
81
|
}
|
|
78
|
-
throw error;
|
|
79
82
|
}
|
|
83
|
+
} catch (error: unknown) {
|
|
84
|
+
if (error instanceof MUDError) {
|
|
85
|
+
error.message = `Function "${name}" in contract "${contractName}": ${error.message}`;
|
|
86
|
+
}
|
|
87
|
+
throw error;
|
|
80
88
|
}
|
|
81
89
|
},
|
|
82
90
|
CustomErrorDefinition({ name, parameters }) {
|
|
83
91
|
errors.push({
|
|
84
|
-
name
|
|
92
|
+
name,
|
|
85
93
|
parameters: parameters.map(parseParameter),
|
|
86
94
|
});
|
|
87
95
|
|
|
@@ -92,10 +100,6 @@ export function contractToInterface(
|
|
|
92
100
|
},
|
|
93
101
|
});
|
|
94
102
|
|
|
95
|
-
if (!withContract) {
|
|
96
|
-
throw new MUDError(`Contract not found: ${contractName}`);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
103
|
return {
|
|
100
104
|
functions,
|
|
101
105
|
errors,
|
|
@@ -103,6 +107,20 @@ export function contractToInterface(
|
|
|
103
107
|
};
|
|
104
108
|
}
|
|
105
109
|
|
|
110
|
+
function findContractNode(ast: SourceUnit, contractName: string): ContractDefinition | undefined {
|
|
111
|
+
let contract = undefined;
|
|
112
|
+
|
|
113
|
+
visit(ast, {
|
|
114
|
+
ContractDefinition(node) {
|
|
115
|
+
if (node.name === contractName) {
|
|
116
|
+
contract = node;
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
return contract;
|
|
122
|
+
}
|
|
123
|
+
|
|
106
124
|
function parseParameter({ name, typeName, storageLocation }: VariableDeclaration): string {
|
|
107
125
|
let typedNameWithLocation = "";
|
|
108
126
|
|
|
@@ -2,10 +2,15 @@ import { parse, visit } from "@solidity-parser/parser";
|
|
|
2
2
|
import { MUDError } from "../../errors";
|
|
3
3
|
|
|
4
4
|
export interface SolidityUserDefinedType {
|
|
5
|
+
/** Fully-qualified name of the user-defined type (may include a library name as prefix) */
|
|
5
6
|
typeId: string;
|
|
7
|
+
/** Name of the wrapped primitive type */
|
|
6
8
|
internalTypeId: string;
|
|
9
|
+
/** Symbol which must be imported to use the type (either the type name, or the library name where the type is defined) */
|
|
7
10
|
importSymbol: string;
|
|
11
|
+
/** Path to the solidity file which contains the user type */
|
|
8
12
|
fromPath: string;
|
|
13
|
+
/** Whether `fromPath` is relative */
|
|
9
14
|
isRelativePath: boolean;
|
|
10
15
|
}
|
|
11
16
|
|
|
@@ -13,6 +18,8 @@ export interface SolidityUserDefinedType {
|
|
|
13
18
|
* Parse the solidity data to extract user-defined type information.
|
|
14
19
|
* @param data contents of a solidity file with the user types declarations
|
|
15
20
|
* @param userTypeNames names of the user types to extract
|
|
21
|
+
* @param fromPath path to the solidity file from which the user types are extracted
|
|
22
|
+
* @returns record of type names mapped to the extracted type information
|
|
16
23
|
*/
|
|
17
24
|
export function extractUserTypes(
|
|
18
25
|
data: string,
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import prettier from "prettier";
|
|
2
2
|
import prettierPluginSolidity from "prettier-plugin-solidity";
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Formats solidity code using prettier
|
|
6
|
+
* @param content solidity code
|
|
7
|
+
* @param prettierConfigPath optional path to a prettier config
|
|
8
|
+
* @returns formatted solidity code
|
|
9
|
+
*/
|
|
4
10
|
export async function formatSolidity(content: string, prettierConfigPath?: string): Promise<string> {
|
|
5
11
|
let config;
|
|
6
12
|
if (prettierConfigPath) {
|
|
@@ -31,6 +37,11 @@ export async function formatSolidity(content: string, prettierConfigPath?: strin
|
|
|
31
37
|
}
|
|
32
38
|
}
|
|
33
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Formats typescript code using prettier
|
|
42
|
+
* @param content typescript code
|
|
43
|
+
* @returns formatted typescript code
|
|
44
|
+
*/
|
|
34
45
|
export async function formatTypescript(content: string): Promise<string> {
|
|
35
46
|
return prettier.format(content, {
|
|
36
47
|
parser: "typescript",
|
|
@@ -3,6 +3,12 @@ import { dirname } from "path";
|
|
|
3
3
|
import { formatSolidity, formatTypescript } from "./format";
|
|
4
4
|
import { debug } from "../debug";
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Formats solidity code using prettier and write it to a file
|
|
8
|
+
* @param output solidity code
|
|
9
|
+
* @param fullOutputPath full path to the output file
|
|
10
|
+
* @param logPrefix prefix for debug logs
|
|
11
|
+
*/
|
|
6
12
|
export async function formatAndWriteSolidity(output: string, fullOutputPath: string, logPrefix: string): Promise<void> {
|
|
7
13
|
const formattedOutput = await formatSolidity(output);
|
|
8
14
|
|
|
@@ -12,6 +18,12 @@ export async function formatAndWriteSolidity(output: string, fullOutputPath: str
|
|
|
12
18
|
debug(`${logPrefix}: ${fullOutputPath}`);
|
|
13
19
|
}
|
|
14
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Formats typescript code using prettier and write it to a file
|
|
23
|
+
* @param output typescript code
|
|
24
|
+
* @param fullOutputPath full path to the output file
|
|
25
|
+
* @param logPrefix prefix for debug logs
|
|
26
|
+
*/
|
|
15
27
|
export async function formatAndWriteTypescript(
|
|
16
28
|
output: string,
|
|
17
29
|
fullOutputPath: string,
|
|
@@ -9,6 +9,13 @@ export type UserType = {
|
|
|
9
9
|
internalType: SchemaAbiType;
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Load the user type files and extract type information from them.
|
|
14
|
+
* @param userTypes record of user type data mapped by type names
|
|
15
|
+
* @param outputBaseDirectory base path to the output directory
|
|
16
|
+
* @param remappings solc remappings
|
|
17
|
+
* @returns record of the user type information mapped by type names
|
|
18
|
+
*/
|
|
12
19
|
export function loadAndExtractUserTypes(
|
|
13
20
|
userTypes: Record<string, UserType>,
|
|
14
21
|
outputBaseDirectory: string,
|
|
@@ -35,7 +42,7 @@ export function loadAndExtractUserTypes(
|
|
|
35
42
|
}
|
|
36
43
|
}
|
|
37
44
|
|
|
38
|
-
extractedUserTypes =
|
|
45
|
+
extractedUserTypes = { ...extractedUserTypes, ...userTypesInFile };
|
|
39
46
|
}
|
|
40
47
|
return extractedUserTypes;
|
|
41
48
|
}
|
package/dist/chunk-TCWGPC6G.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/debug.ts"],"sourcesContent":["import createDebug from \"debug\";\n\nexport const debug = createDebug(\"mud:common\");\nexport const error = createDebug(\"mud:common\");\n\n// Pipe debug output to stdout instead of stderr\ndebug.log = console.debug.bind(console);\n\n// Pipe error output to stderr\nerror.log = console.error.bind(console);\n"],"mappings":"AAAA,OAAOA,MAAiB,QAEjB,IAAMC,EAAQD,EAAY,YAAY,EAChCE,EAAQF,EAAY,YAAY,EAG7CC,EAAM,IAAM,QAAQ,MAAM,KAAK,OAAO,EAGtCC,EAAM,IAAM,QAAQ,MAAM,KAAK,OAAO","names":["createDebug","debug","error"]}
|