@seljs/env 1.0.0 → 1.0.1
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/CHANGELOG.md +7 -0
- package/dist/builder.cjs +220 -0
- package/dist/builder.d.cts +39 -0
- package/dist/builder.d.mts +39 -0
- package/dist/builder.mjs +219 -0
- package/dist/builtins.cjs +373 -0
- package/dist/{builtins.d.ts → builtins.d.cts} +8 -5
- package/dist/builtins.d.mts +20 -0
- package/dist/builtins.mjs +371 -0
- package/dist/index.cjs +8 -0
- package/dist/index.d.cts +3 -0
- package/dist/index.d.mts +3 -0
- package/dist/index.mjs +3 -0
- package/package.json +19 -12
- package/dist/builder.d.ts +0 -36
- package/dist/builder.d.ts.map +0 -1
- package/dist/builder.js +0 -223
- package/dist/builder.spec.d.ts +0 -2
- package/dist/builder.spec.d.ts.map +0 -1
- package/dist/builder.spec.js +0 -428
- package/dist/builtins.d.ts.map +0 -1
- package/dist/builtins.js +0 -307
- package/dist/index.d.ts +0 -3
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -2
package/dist/builder.js
DELETED
|
@@ -1,223 +0,0 @@
|
|
|
1
|
-
import { structTypeName } from "@seljs/common";
|
|
2
|
-
import { mapSolidityTypeToCEL } from "@seljs/types";
|
|
3
|
-
import { CEL_BUILTIN_FUNCTIONS, CEL_BUILTIN_MACROS, SOLIDITY_PRIMITIVE_TYPES, } from "./builtins.js";
|
|
4
|
-
/**
|
|
5
|
-
* Checks if the given ABI parameter is a tuple type with components, indicating that it represents a struct in Solidity.
|
|
6
|
-
* @param param
|
|
7
|
-
*/
|
|
8
|
-
const isTupleParam = (param) => param.type === "tuple" && "components" in param;
|
|
9
|
-
/**
|
|
10
|
-
* Checks if the given ABI parameter is an array of tuples with components, indicating that it represents an array of structs in Solidity.
|
|
11
|
-
* @param param
|
|
12
|
-
*/
|
|
13
|
-
const isTupleArrayParam = (param) => param.type === "tuple[]" && "components" in param;
|
|
14
|
-
/**
|
|
15
|
-
* Checks if the given ABI parameter is either a tuple or an array of tuples, both of which indicate the presence of components and thus represent struct types in Solidity.
|
|
16
|
-
*
|
|
17
|
-
* @param param
|
|
18
|
-
*/
|
|
19
|
-
const isAnyTupleParam = (param) => isTupleParam(param) || isTupleArrayParam(param);
|
|
20
|
-
/**
|
|
21
|
-
* Generates a unique type name for a nested struct based on the parent type name and the field name, used for naming struct types in the SEL schema.
|
|
22
|
-
*
|
|
23
|
-
* @param parentTypeName Name of the parent type (e.g., contract name or parent struct name) to which the field belongs, used as a prefix for the nested struct type name.
|
|
24
|
-
* @param fieldName Name of the field for which to generate the nested struct type name, used as a suffix in the nested struct type name.
|
|
25
|
-
*/
|
|
26
|
-
const nestedTypeName = (parentTypeName, fieldName) => `${parentTypeName}__${fieldName}`;
|
|
27
|
-
/**
|
|
28
|
-
* Recursively builds struct field definitions.
|
|
29
|
-
*
|
|
30
|
-
* @param components Array of ABI parameters representing the components of a tuple type.
|
|
31
|
-
* @param parentTypeName Name to use for the struct type representing the current level of nesting.
|
|
32
|
-
*/
|
|
33
|
-
export const buildStructFields = (components, parentTypeName) => {
|
|
34
|
-
const fields = {};
|
|
35
|
-
const nestedTypes = [];
|
|
36
|
-
for (const component of components) {
|
|
37
|
-
const fieldName = component.name ?? "arg0";
|
|
38
|
-
if (isTupleParam(component) && component.components.length > 0) {
|
|
39
|
-
const childTypeName = nestedTypeName(parentTypeName, fieldName);
|
|
40
|
-
const child = buildStructFields(component.components, childTypeName);
|
|
41
|
-
nestedTypes.push(...child.nestedTypes);
|
|
42
|
-
nestedTypes.push({ typeName: childTypeName, fields: child.fields });
|
|
43
|
-
fields[fieldName] = childTypeName;
|
|
44
|
-
continue;
|
|
45
|
-
}
|
|
46
|
-
if (isTupleArrayParam(component) && component.components.length > 0) {
|
|
47
|
-
const childTypeName = nestedTypeName(parentTypeName, fieldName);
|
|
48
|
-
const child = buildStructFields(component.components, childTypeName);
|
|
49
|
-
nestedTypes.push(...child.nestedTypes);
|
|
50
|
-
nestedTypes.push({ typeName: childTypeName, fields: child.fields });
|
|
51
|
-
fields[fieldName] = `list<${childTypeName}>`;
|
|
52
|
-
continue;
|
|
53
|
-
}
|
|
54
|
-
// For primitive types, directly map to CEL type or use "dyn" if mapping is not possible.
|
|
55
|
-
fields[fieldName] = mapSolidityTypeToCEL(component.type) ?? "dyn";
|
|
56
|
-
}
|
|
57
|
-
return { fields, nestedTypes };
|
|
58
|
-
};
|
|
59
|
-
/**
|
|
60
|
-
* Builds method parameters for a given array of ABI parameters.
|
|
61
|
-
*
|
|
62
|
-
* @param inputs Array of ABI parameters representing the inputs of a function.
|
|
63
|
-
*/
|
|
64
|
-
const buildMethodParams = (inputs) => {
|
|
65
|
-
return inputs.map((param, index) => ({
|
|
66
|
-
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- empty string name means unnamed param
|
|
67
|
-
name: param.name || `arg${String(index)}`,
|
|
68
|
-
type: mapSolidityTypeToCEL(param.type) ?? "dyn",
|
|
69
|
-
}));
|
|
70
|
-
};
|
|
71
|
-
/**
|
|
72
|
-
* Builds the return type resolution for a method based on its ABI components, handling nested structs as needed.
|
|
73
|
-
*
|
|
74
|
-
* @param components Array of ABI parameters representing the components of a tuple return type.
|
|
75
|
-
* @param typeName Name to use for the struct type representing the return value, used for naming nested struct types as well.
|
|
76
|
-
*/
|
|
77
|
-
const buildStructReturn = (components, typeName) => {
|
|
78
|
-
const result = buildStructFields(components, typeName);
|
|
79
|
-
const structTypes = [];
|
|
80
|
-
for (const nested of result.nestedTypes) {
|
|
81
|
-
structTypes.push({
|
|
82
|
-
name: nested.typeName,
|
|
83
|
-
kind: "struct",
|
|
84
|
-
fields: Object.entries(nested.fields).map(([name, type]) => ({
|
|
85
|
-
name,
|
|
86
|
-
type,
|
|
87
|
-
})),
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
structTypes.push({
|
|
91
|
-
name: typeName,
|
|
92
|
-
kind: "struct",
|
|
93
|
-
fields: Object.entries(result.fields).map(([name, type]) => ({
|
|
94
|
-
name,
|
|
95
|
-
type,
|
|
96
|
-
})),
|
|
97
|
-
});
|
|
98
|
-
return { returns: typeName, structTypes };
|
|
99
|
-
};
|
|
100
|
-
/**
|
|
101
|
-
* Resolves the return type of method based on its ABI function definition.
|
|
102
|
-
*
|
|
103
|
-
* @param fn ABI function for which to resolve the return type.
|
|
104
|
-
* @param contractName Name of the contract to which the function belongs, used for naming nested struct types.
|
|
105
|
-
*/
|
|
106
|
-
const resolveMethodReturnType = (fn, contractName) => {
|
|
107
|
-
const firstOutput = fn.outputs[0];
|
|
108
|
-
if (!firstOutput) {
|
|
109
|
-
return { returns: "dyn", structTypes: [] };
|
|
110
|
-
}
|
|
111
|
-
/*
|
|
112
|
-
* This is the most basic case:
|
|
113
|
-
* Single non-tuple return value can be directly mapped to a CEL type without needing to define a struct.
|
|
114
|
-
*/
|
|
115
|
-
if (fn.outputs.length === 1 && !isAnyTupleParam(firstOutput)) {
|
|
116
|
-
const celType = mapSolidityTypeToCEL(firstOutput.type) ?? "dyn";
|
|
117
|
-
return { returns: celType, structTypes: [] };
|
|
118
|
-
}
|
|
119
|
-
const typeName = structTypeName(contractName, fn.name);
|
|
120
|
-
// For more complex cases, we need to define struct types.
|
|
121
|
-
const isSingleOutput = fn.outputs.length === 1;
|
|
122
|
-
const isSingleTupleArray = isSingleOutput && isTupleArrayParam(firstOutput);
|
|
123
|
-
const isSingleTuple = isSingleOutput && isTupleParam(firstOutput);
|
|
124
|
-
let components;
|
|
125
|
-
if (isSingleTuple || isSingleTupleArray) {
|
|
126
|
-
components = firstOutput.components;
|
|
127
|
-
}
|
|
128
|
-
else {
|
|
129
|
-
components = fn.outputs.map((o, i) => ({
|
|
130
|
-
...o,
|
|
131
|
-
name: o.name ?? `arg${String(i)}`,
|
|
132
|
-
}));
|
|
133
|
-
}
|
|
134
|
-
// Build the struct return type and collect any nested struct types needed for the return value.
|
|
135
|
-
const resolution = buildStructReturn(components, typeName);
|
|
136
|
-
if (isSingleTupleArray) {
|
|
137
|
-
return {
|
|
138
|
-
returns: `list<${typeName}>`,
|
|
139
|
-
structTypes: resolution.structTypes,
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
else {
|
|
143
|
-
return resolution;
|
|
144
|
-
}
|
|
145
|
-
};
|
|
146
|
-
/**
|
|
147
|
-
* Builds a MethodSchema for a given ABI function.
|
|
148
|
-
*
|
|
149
|
-
* @param fn ABI function for which to build the MethodSchema.
|
|
150
|
-
* @param contractName Name of the contract to which the function belongs, used for naming nested struct types.
|
|
151
|
-
* @param acc Accumulator to keep track of seen struct types and collect unique TypeSchemas for nested structs.
|
|
152
|
-
*/
|
|
153
|
-
const buildMethodSchema = (fn, contractName, acc) => {
|
|
154
|
-
const params = buildMethodParams(fn.inputs);
|
|
155
|
-
// Handle the return type resolution.
|
|
156
|
-
const resolution = resolveMethodReturnType(fn, contractName);
|
|
157
|
-
// Add any struct types discovered during return type resolution to the accumulator, ensuring uniqueness.
|
|
158
|
-
for (const st of resolution.structTypes) {
|
|
159
|
-
if (!acc.seen.has(st.name)) {
|
|
160
|
-
acc.types.push(st);
|
|
161
|
-
acc.seen.add(st.name);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
return { name: fn.name, params, returns: resolution.returns, abi: fn };
|
|
165
|
-
};
|
|
166
|
-
/**
|
|
167
|
-
* Builds a ContractSchema for a given contract by processing its ABI and extracting view/pure functions as methods.
|
|
168
|
-
*
|
|
169
|
-
* @param contractName Name of the contract for which to build the schema.
|
|
170
|
-
* @param input ContractInput containing the ABI and other relevant information for the contract.
|
|
171
|
-
* @param acc Accumulator to keep track of seen struct types and collect unique TypeSchemas for nested structs.
|
|
172
|
-
*/
|
|
173
|
-
const buildContractSchema = (contractName, input, acc) => {
|
|
174
|
-
// Filter ABI to include only view and pure functions, which are relevant for read-only interactions in the SEL environment.
|
|
175
|
-
const viewFunctions = input.abi.filter((item) => item.type === "function" &&
|
|
176
|
-
(item.stateMutability === "view" || item.stateMutability === "pure"));
|
|
177
|
-
const methods = viewFunctions.map((fn) => buildMethodSchema(fn, contractName, acc));
|
|
178
|
-
return {
|
|
179
|
-
name: contractName,
|
|
180
|
-
address: input.address,
|
|
181
|
-
description: input.description,
|
|
182
|
-
methods,
|
|
183
|
-
};
|
|
184
|
-
};
|
|
185
|
-
/**
|
|
186
|
-
* Resolves a ContextFieldDefinition to its CEL type string.
|
|
187
|
-
*/
|
|
188
|
-
const resolveFieldType = (field) => typeof field === "string" ? field : field.type;
|
|
189
|
-
/**
|
|
190
|
-
* Resolves the optional description from a ContextFieldDefinition.
|
|
191
|
-
*/
|
|
192
|
-
const resolveFieldDescription = (field) => typeof field === "string" ? undefined : field.description;
|
|
193
|
-
/**
|
|
194
|
-
* Extracts variables from the provided context definition and builds an array of VariableSchema objects.
|
|
195
|
-
*/
|
|
196
|
-
const buildVariablesFromContext = (config) => {
|
|
197
|
-
if (!config.context) {
|
|
198
|
-
return [];
|
|
199
|
-
}
|
|
200
|
-
return Object.entries(config.context).map(([name, field]) => ({
|
|
201
|
-
name,
|
|
202
|
-
type: resolveFieldType(field),
|
|
203
|
-
description: resolveFieldDescription(field),
|
|
204
|
-
}));
|
|
205
|
-
};
|
|
206
|
-
/**
|
|
207
|
-
* Builds a SELSchema based on the provided configuration, which includes contract definitions and an optional context for variables.
|
|
208
|
-
*
|
|
209
|
-
* @param config Configuration object containing contract definitions and an optional context for variables.
|
|
210
|
-
* @returns A SELSchema object representing the schema for the SEL environment.
|
|
211
|
-
*/
|
|
212
|
-
export const buildSchema = (config) => {
|
|
213
|
-
const acc = { seen: new Set(), types: [] };
|
|
214
|
-
const contracts = Object.entries(config.contracts ?? {}).map(([name, input]) => buildContractSchema(name, input, acc));
|
|
215
|
-
return {
|
|
216
|
-
version: "1.0.0",
|
|
217
|
-
contracts,
|
|
218
|
-
variables: buildVariablesFromContext(config),
|
|
219
|
-
types: [...SOLIDITY_PRIMITIVE_TYPES, ...acc.types],
|
|
220
|
-
functions: CEL_BUILTIN_FUNCTIONS,
|
|
221
|
-
macros: CEL_BUILTIN_MACROS,
|
|
222
|
-
};
|
|
223
|
-
};
|
package/dist/builder.spec.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"builder.spec.d.ts","sourceRoot":"","sources":["../src/builder.spec.ts"],"names":[],"mappings":""}
|
package/dist/builder.spec.js
DELETED
|
@@ -1,428 +0,0 @@
|
|
|
1
|
-
import { parseAbi } from "viem";
|
|
2
|
-
import { describe, expect, it } from "vitest";
|
|
3
|
-
import { buildSchema } from "./builder.js";
|
|
4
|
-
const SIMPLE_ABI = [
|
|
5
|
-
{
|
|
6
|
-
type: "function",
|
|
7
|
-
name: "balanceOf",
|
|
8
|
-
stateMutability: "view",
|
|
9
|
-
inputs: [{ name: "account", type: "address" }],
|
|
10
|
-
outputs: [{ name: "", type: "uint256" }],
|
|
11
|
-
},
|
|
12
|
-
{
|
|
13
|
-
type: "function",
|
|
14
|
-
name: "totalSupply",
|
|
15
|
-
stateMutability: "view",
|
|
16
|
-
inputs: [],
|
|
17
|
-
outputs: [{ name: "", type: "uint256" }],
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
type: "function",
|
|
21
|
-
name: "name",
|
|
22
|
-
stateMutability: "view",
|
|
23
|
-
inputs: [],
|
|
24
|
-
outputs: [{ name: "", type: "string" }],
|
|
25
|
-
},
|
|
26
|
-
];
|
|
27
|
-
/**
|
|
28
|
-
* Comprehensive ABI covering all struct return scenarios:
|
|
29
|
-
* - tuple return (getPool)
|
|
30
|
-
* - multi-return (getReserves)
|
|
31
|
-
* - nested tuple (getInfo)
|
|
32
|
-
* - tuple[] return (getPositions)
|
|
33
|
-
* - tuple with tuple[] field (getAccount)
|
|
34
|
-
*/
|
|
35
|
-
const STRUCT_ABI = [
|
|
36
|
-
{
|
|
37
|
-
type: "function",
|
|
38
|
-
name: "getPool",
|
|
39
|
-
stateMutability: "view",
|
|
40
|
-
inputs: [{ name: "id", type: "uint256" }],
|
|
41
|
-
outputs: [
|
|
42
|
-
{
|
|
43
|
-
name: "",
|
|
44
|
-
type: "tuple",
|
|
45
|
-
components: [
|
|
46
|
-
{ name: "token", type: "address" },
|
|
47
|
-
{ name: "balance", type: "uint256" },
|
|
48
|
-
],
|
|
49
|
-
},
|
|
50
|
-
],
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
type: "function",
|
|
54
|
-
name: "getReserves",
|
|
55
|
-
stateMutability: "view",
|
|
56
|
-
inputs: [],
|
|
57
|
-
outputs: [
|
|
58
|
-
{ name: "reserve0", type: "uint112" },
|
|
59
|
-
{ name: "reserve1", type: "uint112" },
|
|
60
|
-
{ name: "blockTimestampLast", type: "uint32" },
|
|
61
|
-
],
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
type: "function",
|
|
65
|
-
name: "getInfo",
|
|
66
|
-
stateMutability: "view",
|
|
67
|
-
inputs: [],
|
|
68
|
-
outputs: [
|
|
69
|
-
{
|
|
70
|
-
name: "",
|
|
71
|
-
type: "tuple",
|
|
72
|
-
components: [
|
|
73
|
-
{ name: "id", type: "uint256" },
|
|
74
|
-
{
|
|
75
|
-
name: "details",
|
|
76
|
-
type: "tuple",
|
|
77
|
-
components: [
|
|
78
|
-
{ name: "token", type: "address" },
|
|
79
|
-
{ name: "amount", type: "uint256" },
|
|
80
|
-
],
|
|
81
|
-
},
|
|
82
|
-
],
|
|
83
|
-
},
|
|
84
|
-
],
|
|
85
|
-
},
|
|
86
|
-
{
|
|
87
|
-
type: "function",
|
|
88
|
-
name: "getPositions",
|
|
89
|
-
stateMutability: "view",
|
|
90
|
-
inputs: [{ name: "user", type: "address" }],
|
|
91
|
-
outputs: [
|
|
92
|
-
{
|
|
93
|
-
name: "",
|
|
94
|
-
type: "tuple[]",
|
|
95
|
-
components: [
|
|
96
|
-
{ name: "token", type: "address" },
|
|
97
|
-
{ name: "amount", type: "uint256" },
|
|
98
|
-
],
|
|
99
|
-
},
|
|
100
|
-
],
|
|
101
|
-
},
|
|
102
|
-
{
|
|
103
|
-
type: "function",
|
|
104
|
-
name: "getAccount",
|
|
105
|
-
stateMutability: "view",
|
|
106
|
-
inputs: [],
|
|
107
|
-
outputs: [
|
|
108
|
-
{
|
|
109
|
-
name: "",
|
|
110
|
-
type: "tuple",
|
|
111
|
-
components: [
|
|
112
|
-
{ name: "owner", type: "address" },
|
|
113
|
-
{
|
|
114
|
-
name: "positions",
|
|
115
|
-
type: "tuple[]",
|
|
116
|
-
components: [
|
|
117
|
-
{ name: "token", type: "address" },
|
|
118
|
-
{ name: "amount", type: "uint256" },
|
|
119
|
-
],
|
|
120
|
-
},
|
|
121
|
-
],
|
|
122
|
-
},
|
|
123
|
-
],
|
|
124
|
-
},
|
|
125
|
-
];
|
|
126
|
-
describe("src/builder.ts", () => {
|
|
127
|
-
describe("contracts", () => {
|
|
128
|
-
it("builds a schema with the correct contract name and method count", () => {
|
|
129
|
-
const schema = buildSchema({
|
|
130
|
-
contracts: {
|
|
131
|
-
erc20: {
|
|
132
|
-
address: "0x6B175474E89094C44Da98b954EedeAC495271d0F",
|
|
133
|
-
abi: SIMPLE_ABI,
|
|
134
|
-
},
|
|
135
|
-
},
|
|
136
|
-
});
|
|
137
|
-
expect(schema.version).toBe("1.0.0");
|
|
138
|
-
expect(schema.contracts).toHaveLength(1);
|
|
139
|
-
expect(schema.contracts[0]?.name).toBe("erc20");
|
|
140
|
-
expect(schema.contracts[0]?.methods).toHaveLength(3);
|
|
141
|
-
});
|
|
142
|
-
it("includes contract description", () => {
|
|
143
|
-
const schema = buildSchema({
|
|
144
|
-
contracts: {
|
|
145
|
-
erc20: {
|
|
146
|
-
address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
147
|
-
abi: SIMPLE_ABI,
|
|
148
|
-
description: "ERC-20 Token",
|
|
149
|
-
},
|
|
150
|
-
},
|
|
151
|
-
});
|
|
152
|
-
expect(schema.contracts[0]?.description).toBe("ERC-20 Token");
|
|
153
|
-
});
|
|
154
|
-
it("handles empty config", () => {
|
|
155
|
-
const schema = buildSchema({});
|
|
156
|
-
expect(schema.contracts).toEqual([]);
|
|
157
|
-
expect(schema.variables).toEqual([]);
|
|
158
|
-
expect(schema.version).toBe("1.0.0");
|
|
159
|
-
});
|
|
160
|
-
it("includes builtin types, functions, and macros", () => {
|
|
161
|
-
const schema = buildSchema({});
|
|
162
|
-
expect(schema.types.length).toBeGreaterThan(0);
|
|
163
|
-
expect(schema.functions.length).toBeGreaterThan(0);
|
|
164
|
-
expect(schema.macros.length).toBeGreaterThan(0);
|
|
165
|
-
});
|
|
166
|
-
it("only includes view and pure functions (excludes nonpayable, payable, events)", () => {
|
|
167
|
-
const schema = buildSchema({
|
|
168
|
-
contracts: {
|
|
169
|
-
c: {
|
|
170
|
-
address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
171
|
-
abi: [
|
|
172
|
-
{
|
|
173
|
-
type: "function",
|
|
174
|
-
name: "viewFn",
|
|
175
|
-
stateMutability: "view",
|
|
176
|
-
inputs: [],
|
|
177
|
-
outputs: [{ name: "", type: "uint256" }],
|
|
178
|
-
},
|
|
179
|
-
{
|
|
180
|
-
type: "function",
|
|
181
|
-
name: "pureFn",
|
|
182
|
-
stateMutability: "pure",
|
|
183
|
-
inputs: [],
|
|
184
|
-
outputs: [{ name: "", type: "bool" }],
|
|
185
|
-
},
|
|
186
|
-
{
|
|
187
|
-
type: "function",
|
|
188
|
-
name: "nonpayableFn",
|
|
189
|
-
stateMutability: "nonpayable",
|
|
190
|
-
inputs: [],
|
|
191
|
-
outputs: [],
|
|
192
|
-
},
|
|
193
|
-
{
|
|
194
|
-
type: "function",
|
|
195
|
-
name: "payableFn",
|
|
196
|
-
stateMutability: "payable",
|
|
197
|
-
inputs: [],
|
|
198
|
-
outputs: [],
|
|
199
|
-
},
|
|
200
|
-
{
|
|
201
|
-
type: "event",
|
|
202
|
-
name: "Transfer",
|
|
203
|
-
inputs: [],
|
|
204
|
-
},
|
|
205
|
-
],
|
|
206
|
-
},
|
|
207
|
-
},
|
|
208
|
-
});
|
|
209
|
-
const methodNames = schema.contracts[0]?.methods.map((m) => m.name);
|
|
210
|
-
expect(methodNames).toContain("viewFn");
|
|
211
|
-
expect(methodNames).toContain("pureFn");
|
|
212
|
-
expect(methodNames).not.toContain("nonpayableFn");
|
|
213
|
-
expect(methodNames).not.toContain("payableFn");
|
|
214
|
-
expect(methodNames).not.toContain("Transfer");
|
|
215
|
-
});
|
|
216
|
-
it("maps unnamed params to arg{index}", () => {
|
|
217
|
-
const schema = buildSchema({
|
|
218
|
-
contracts: {
|
|
219
|
-
c: {
|
|
220
|
-
address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
221
|
-
abi: [
|
|
222
|
-
{
|
|
223
|
-
type: "function",
|
|
224
|
-
name: "foo",
|
|
225
|
-
stateMutability: "view",
|
|
226
|
-
inputs: [
|
|
227
|
-
{ name: "", type: "address" },
|
|
228
|
-
{ name: "", type: "uint256" },
|
|
229
|
-
],
|
|
230
|
-
outputs: [{ name: "", type: "bool" }],
|
|
231
|
-
},
|
|
232
|
-
],
|
|
233
|
-
},
|
|
234
|
-
},
|
|
235
|
-
});
|
|
236
|
-
const params = schema.contracts[0]?.methods[0]?.params;
|
|
237
|
-
expect(params?.[0]?.name).toBe("arg0");
|
|
238
|
-
expect(params?.[1]?.name).toBe("arg1");
|
|
239
|
-
});
|
|
240
|
-
});
|
|
241
|
-
describe("method param and return types (CEL types)", () => {
|
|
242
|
-
const schema = buildSchema({
|
|
243
|
-
contracts: {
|
|
244
|
-
token: {
|
|
245
|
-
address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
246
|
-
abi: SIMPLE_ABI,
|
|
247
|
-
},
|
|
248
|
-
},
|
|
249
|
-
});
|
|
250
|
-
it("maps address param to sol_address", () => {
|
|
251
|
-
const method = schema.contracts[0]?.methods.find((m) => m.name === "balanceOf");
|
|
252
|
-
expect(method?.params[0]?.name).toBe("account");
|
|
253
|
-
expect(method?.params[0]?.type).toBe("sol_address");
|
|
254
|
-
});
|
|
255
|
-
it.each([
|
|
256
|
-
["balanceOf", "sol_int"],
|
|
257
|
-
["totalSupply", "sol_int"],
|
|
258
|
-
["name", "string"],
|
|
259
|
-
])("%s returns %s", (methodName, expectedReturn) => {
|
|
260
|
-
const method = schema.contracts[0]?.methods.find((m) => m.name === methodName);
|
|
261
|
-
expect(method?.returns).toBe(expectedReturn);
|
|
262
|
-
});
|
|
263
|
-
});
|
|
264
|
-
describe("context variables", () => {
|
|
265
|
-
it("builds a schema with context variables", () => {
|
|
266
|
-
const schema = buildSchema({
|
|
267
|
-
context: {
|
|
268
|
-
user: "sol_address",
|
|
269
|
-
amount: "sol_int",
|
|
270
|
-
},
|
|
271
|
-
});
|
|
272
|
-
expect(schema.variables).toHaveLength(2);
|
|
273
|
-
expect(schema.variables).toEqual(expect.arrayContaining([
|
|
274
|
-
expect.objectContaining({ name: "user", type: "sol_address" }),
|
|
275
|
-
expect.objectContaining({ name: "amount", type: "sol_int" }),
|
|
276
|
-
]));
|
|
277
|
-
});
|
|
278
|
-
it("builds a schema with both contracts and context", () => {
|
|
279
|
-
const schema = buildSchema({
|
|
280
|
-
contracts: {
|
|
281
|
-
erc20: {
|
|
282
|
-
address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
283
|
-
abi: SIMPLE_ABI,
|
|
284
|
-
},
|
|
285
|
-
},
|
|
286
|
-
context: {
|
|
287
|
-
user: "sol_address",
|
|
288
|
-
},
|
|
289
|
-
});
|
|
290
|
-
expect(schema.contracts).toHaveLength(1);
|
|
291
|
-
expect(schema.variables).toHaveLength(1);
|
|
292
|
-
});
|
|
293
|
-
});
|
|
294
|
-
describe("struct returns", () => {
|
|
295
|
-
const schema = buildSchema({
|
|
296
|
-
contracts: {
|
|
297
|
-
vault: {
|
|
298
|
-
address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
299
|
-
abi: STRUCT_ABI,
|
|
300
|
-
},
|
|
301
|
-
},
|
|
302
|
-
});
|
|
303
|
-
it("generates TypeSchema for struct-returning method", () => {
|
|
304
|
-
const structType = schema.types.find((t) => t.name === "SEL_Struct_vault_getPool");
|
|
305
|
-
expect(structType).toBeDefined();
|
|
306
|
-
expect(structType?.kind).toBe("struct");
|
|
307
|
-
});
|
|
308
|
-
it("sets method returns to struct type name", () => {
|
|
309
|
-
const method = schema.contracts[0]?.methods.find((m) => m.name === "getPool");
|
|
310
|
-
expect(method?.returns).toBe("SEL_Struct_vault_getPool");
|
|
311
|
-
});
|
|
312
|
-
it("struct fields use CEL types (sol_address, sol_int)", () => {
|
|
313
|
-
const structType = schema.types.find((t) => t.name === "SEL_Struct_vault_getPool");
|
|
314
|
-
expect(structType?.fields).toEqual(expect.arrayContaining([
|
|
315
|
-
{ name: "token", type: "sol_address" },
|
|
316
|
-
{ name: "balance", type: "sol_int" },
|
|
317
|
-
]));
|
|
318
|
-
});
|
|
319
|
-
it("generates TypeSchema for multi-return method", () => {
|
|
320
|
-
const structType = schema.types.find((t) => t.name === "SEL_Struct_vault_getReserves");
|
|
321
|
-
expect(structType).toBeDefined();
|
|
322
|
-
expect(structType?.kind).toBe("struct");
|
|
323
|
-
expect(structType?.fields).toHaveLength(3);
|
|
324
|
-
expect(structType?.fields).toEqual(expect.arrayContaining([
|
|
325
|
-
{ name: "reserve0", type: "sol_int" },
|
|
326
|
-
{ name: "reserve1", type: "sol_int" },
|
|
327
|
-
{ name: "blockTimestampLast", type: "sol_int" },
|
|
328
|
-
]));
|
|
329
|
-
});
|
|
330
|
-
it("does not generate struct types for simple returns", () => {
|
|
331
|
-
const simpleSchema = buildSchema({
|
|
332
|
-
contracts: {
|
|
333
|
-
erc20: {
|
|
334
|
-
address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
335
|
-
abi: SIMPLE_ABI,
|
|
336
|
-
},
|
|
337
|
-
},
|
|
338
|
-
});
|
|
339
|
-
const structTypes = simpleSchema.types.filter((t) => t.kind === "struct");
|
|
340
|
-
expect(structTypes).toHaveLength(0);
|
|
341
|
-
});
|
|
342
|
-
it("emits struct type name for struct method and CEL type for simple method", () => {
|
|
343
|
-
const mixedSchema = buildSchema({
|
|
344
|
-
contracts: {
|
|
345
|
-
token: {
|
|
346
|
-
address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
347
|
-
abi: SIMPLE_ABI,
|
|
348
|
-
},
|
|
349
|
-
pool: {
|
|
350
|
-
address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
351
|
-
abi: STRUCT_ABI,
|
|
352
|
-
},
|
|
353
|
-
},
|
|
354
|
-
});
|
|
355
|
-
const balanceOf = mixedSchema.contracts
|
|
356
|
-
.find((c) => c.name === "token")
|
|
357
|
-
?.methods.find((m) => m.name === "balanceOf");
|
|
358
|
-
expect(balanceOf?.returns).toBe("sol_int");
|
|
359
|
-
const getPool = mixedSchema.contracts
|
|
360
|
-
.find((c) => c.name === "pool")
|
|
361
|
-
?.methods.find((m) => m.name === "getPool");
|
|
362
|
-
expect(getPool?.returns).toBe("SEL_Struct_pool_getPool");
|
|
363
|
-
});
|
|
364
|
-
it("generates nested struct TypeSchema entries", () => {
|
|
365
|
-
const parentType = schema.types.find((t) => t.name === "SEL_Struct_vault_getInfo");
|
|
366
|
-
expect(parentType).toBeDefined();
|
|
367
|
-
expect(parentType?.kind).toBe("struct");
|
|
368
|
-
const nestedType = schema.types.find((t) => t.name === "SEL_Struct_vault_getInfo__details");
|
|
369
|
-
expect(nestedType).toBeDefined();
|
|
370
|
-
expect(nestedType?.kind).toBe("struct");
|
|
371
|
-
expect(nestedType?.fields).toEqual(expect.arrayContaining([
|
|
372
|
-
{ name: "token", type: "sol_address" },
|
|
373
|
-
{ name: "amount", type: "sol_int" },
|
|
374
|
-
]));
|
|
375
|
-
const detailsField = parentType?.fields?.find((f) => f.name === "details");
|
|
376
|
-
expect(detailsField?.type).toBe("SEL_Struct_vault_getInfo__details");
|
|
377
|
-
});
|
|
378
|
-
it("generates list<struct> return for tuple[] method", () => {
|
|
379
|
-
const method = schema.contracts[0]?.methods.find((m) => m.name === "getPositions");
|
|
380
|
-
expect(method?.returns).toBe("list<SEL_Struct_vault_getPositions>");
|
|
381
|
-
});
|
|
382
|
-
it("registers struct type for tuple[] return", () => {
|
|
383
|
-
const structType = schema.types.find((t) => t.name === "SEL_Struct_vault_getPositions");
|
|
384
|
-
expect(structType).toBeDefined();
|
|
385
|
-
expect(structType?.kind).toBe("struct");
|
|
386
|
-
expect(structType?.fields).toEqual(expect.arrayContaining([
|
|
387
|
-
{ name: "token", type: "sol_address" },
|
|
388
|
-
{ name: "amount", type: "sol_int" },
|
|
389
|
-
]));
|
|
390
|
-
});
|
|
391
|
-
it("resolves tuple[] field inside struct to list<nested struct>", () => {
|
|
392
|
-
const parentType = schema.types.find((t) => t.name === "SEL_Struct_vault_getAccount");
|
|
393
|
-
const positionsField = parentType?.fields?.find((f) => f.name === "positions");
|
|
394
|
-
expect(positionsField?.type).toBe("list<SEL_Struct_vault_getAccount__positions>");
|
|
395
|
-
});
|
|
396
|
-
it("registers nested struct type for tuple[] field", () => {
|
|
397
|
-
const nestedType = schema.types.find((t) => t.name === "SEL_Struct_vault_getAccount__positions");
|
|
398
|
-
expect(nestedType).toBeDefined();
|
|
399
|
-
expect(nestedType?.kind).toBe("struct");
|
|
400
|
-
expect(nestedType?.fields).toEqual(expect.arrayContaining([
|
|
401
|
-
{ name: "token", type: "sol_address" },
|
|
402
|
-
{ name: "amount", type: "sol_int" },
|
|
403
|
-
]));
|
|
404
|
-
});
|
|
405
|
-
it("nested struct types appear before parent in types array", () => {
|
|
406
|
-
const typeNames = schema.types.map((t) => t.name);
|
|
407
|
-
const nestedIdx = typeNames.indexOf("SEL_Struct_vault_getInfo__details");
|
|
408
|
-
const parentIdx = typeNames.indexOf("SEL_Struct_vault_getInfo");
|
|
409
|
-
expect(nestedIdx).toBeGreaterThan(-1);
|
|
410
|
-
expect(parentIdx).toBeGreaterThan(-1);
|
|
411
|
-
expect(nestedIdx).toBeLessThan(parentIdx);
|
|
412
|
-
});
|
|
413
|
-
it("includes AbiFunction on method schema", () => {
|
|
414
|
-
const abi = parseAbi([
|
|
415
|
-
"function balanceOf(address owner) view returns (uint256)",
|
|
416
|
-
]);
|
|
417
|
-
const schema = buildSchema({
|
|
418
|
-
contracts: {
|
|
419
|
-
token: { address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", abi },
|
|
420
|
-
},
|
|
421
|
-
});
|
|
422
|
-
const method = schema.contracts[0].methods[0];
|
|
423
|
-
expect(method.abi).toBeDefined();
|
|
424
|
-
expect(method.abi.name).toBe("balanceOf");
|
|
425
|
-
expect(method.abi.type).toBe("function");
|
|
426
|
-
});
|
|
427
|
-
});
|
|
428
|
-
});
|
package/dist/builtins.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"builtins.d.ts","sourceRoot":"","sources":["../src/builtins.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE7E;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,cAAc,EAsNjD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,EAAE,WAAW,EAiD3C,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,wBAAwB,EAAE,UAAU,EAkChD,CAAC"}
|