@account-kit/plugingen 4.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 (171) hide show
  1. package/LICENSE +21 -0
  2. package/dist/esm/IPlugin.d.ts +474 -0
  3. package/dist/esm/IPlugin.js +397 -0
  4. package/dist/esm/IPlugin.js.map +1 -0
  5. package/dist/esm/cli.d.ts +2 -0
  6. package/dist/esm/cli.js +40 -0
  7. package/dist/esm/cli.js.map +1 -0
  8. package/dist/esm/commands/generate/index.d.ts +14 -0
  9. package/dist/esm/commands/generate/index.js +179 -0
  10. package/dist/esm/commands/generate/index.js.map +1 -0
  11. package/dist/esm/commands/generate/phases/contract-abi-gen.d.ts +2 -0
  12. package/dist/esm/commands/generate/phases/contract-abi-gen.js +9 -0
  13. package/dist/esm/commands/generate/phases/contract-abi-gen.js.map +1 -0
  14. package/dist/esm/commands/generate/phases/contract-addresses-gen.d.ts +2 -0
  15. package/dist/esm/commands/generate/phases/contract-addresses-gen.js +10 -0
  16. package/dist/esm/commands/generate/phases/contract-addresses-gen.js.map +1 -0
  17. package/dist/esm/commands/generate/phases/execution-abi-gen.d.ts +2 -0
  18. package/dist/esm/commands/generate/phases/execution-abi-gen.js +12 -0
  19. package/dist/esm/commands/generate/phases/execution-abi-gen.js.map +1 -0
  20. package/dist/esm/commands/generate/phases/plugin-actions/index.d.ts +2 -0
  21. package/dist/esm/commands/generate/phases/plugin-actions/index.js +118 -0
  22. package/dist/esm/commands/generate/phases/plugin-actions/index.js.map +1 -0
  23. package/dist/esm/commands/generate/phases/plugin-actions/management-actions.d.ts +2 -0
  24. package/dist/esm/commands/generate/phases/plugin-actions/management-actions.js +105 -0
  25. package/dist/esm/commands/generate/phases/plugin-actions/management-actions.js.map +1 -0
  26. package/dist/esm/commands/generate/phases/plugin-actions/read-actions.d.ts +2 -0
  27. package/dist/esm/commands/generate/phases/plugin-actions/read-actions.js +75 -0
  28. package/dist/esm/commands/generate/phases/plugin-actions/read-actions.js.map +1 -0
  29. package/dist/esm/commands/generate/phases/plugin-generator/get-contract-gen.d.ts +2 -0
  30. package/dist/esm/commands/generate/phases/plugin-generator/get-contract-gen.js +26 -0
  31. package/dist/esm/commands/generate/phases/plugin-generator/get-contract-gen.js.map +1 -0
  32. package/dist/esm/commands/generate/phases/plugin-generator/index.d.ts +2 -0
  33. package/dist/esm/commands/generate/phases/plugin-generator/index.js +20 -0
  34. package/dist/esm/commands/generate/phases/plugin-generator/index.js.map +1 -0
  35. package/dist/esm/commands/generate/phases/plugin-generator/meta-gen.d.ts +2 -0
  36. package/dist/esm/commands/generate/phases/plugin-generator/meta-gen.js +14 -0
  37. package/dist/esm/commands/generate/phases/plugin-generator/meta-gen.js.map +1 -0
  38. package/dist/esm/commands/generate/types.d.ts +16 -0
  39. package/dist/esm/commands/generate/types.js +2 -0
  40. package/dist/esm/commands/generate/types.js.map +1 -0
  41. package/dist/esm/commands/generate/utils.d.ts +4 -0
  42. package/dist/esm/commands/generate/utils.js +16 -0
  43. package/dist/esm/commands/generate/utils.js.map +1 -0
  44. package/dist/esm/commands/init.d.ts +5 -0
  45. package/dist/esm/commands/init.js +76 -0
  46. package/dist/esm/commands/init.js.map +1 -0
  47. package/dist/esm/config.d.ts +36 -0
  48. package/dist/esm/config.js +4 -0
  49. package/dist/esm/config.js.map +1 -0
  50. package/dist/esm/errors.d.ts +14 -0
  51. package/dist/esm/errors.js +37 -0
  52. package/dist/esm/errors.js.map +1 -0
  53. package/dist/esm/exports/config.d.ts +1 -0
  54. package/dist/esm/exports/config.js +2 -0
  55. package/dist/esm/exports/config.js.map +1 -0
  56. package/dist/esm/exports/index.d.ts +4 -0
  57. package/dist/esm/exports/index.js +5 -0
  58. package/dist/esm/exports/index.js.map +1 -0
  59. package/dist/esm/exports/types.d.ts +1 -0
  60. package/dist/esm/exports/types.js +2 -0
  61. package/dist/esm/exports/types.js.map +1 -0
  62. package/dist/esm/logger.d.ts +6 -0
  63. package/dist/esm/logger.js +30 -0
  64. package/dist/esm/logger.js.map +1 -0
  65. package/dist/esm/package.json +1 -0
  66. package/dist/esm/types.d.ts +6 -0
  67. package/dist/esm/types.js +2 -0
  68. package/dist/esm/types.js.map +1 -0
  69. package/dist/esm/utils/findConfig.d.ts +6 -0
  70. package/dist/esm/utils/findConfig.js +21 -0
  71. package/dist/esm/utils/findConfig.js.map +1 -0
  72. package/dist/esm/utils/format.d.ts +1 -0
  73. package/dist/esm/utils/format.js +10 -0
  74. package/dist/esm/utils/format.js.map +1 -0
  75. package/dist/esm/utils/isUsingTypescript.d.ts +1 -0
  76. package/dist/esm/utils/isUsingTypescript.js +19 -0
  77. package/dist/esm/utils/isUsingTypescript.js.map +1 -0
  78. package/dist/esm/utils/loadEnv.d.ts +4 -0
  79. package/dist/esm/utils/loadEnv.js +59 -0
  80. package/dist/esm/utils/loadEnv.js.map +1 -0
  81. package/dist/esm/utils/resolveConfig.d.ts +7 -0
  82. package/dist/esm/utils/resolveConfig.js +12 -0
  83. package/dist/esm/utils/resolveConfig.js.map +1 -0
  84. package/dist/esm/version.d.ts +1 -0
  85. package/dist/esm/version.js +2 -0
  86. package/dist/esm/version.js.map +1 -0
  87. package/dist/types/IPlugin.d.ts +475 -0
  88. package/dist/types/IPlugin.d.ts.map +1 -0
  89. package/dist/types/cli.d.ts +3 -0
  90. package/dist/types/cli.d.ts.map +1 -0
  91. package/dist/types/commands/generate/index.d.ts +17 -0
  92. package/dist/types/commands/generate/index.d.ts.map +1 -0
  93. package/dist/types/commands/generate/phases/contract-abi-gen.d.ts +3 -0
  94. package/dist/types/commands/generate/phases/contract-abi-gen.d.ts.map +1 -0
  95. package/dist/types/commands/generate/phases/contract-addresses-gen.d.ts +3 -0
  96. package/dist/types/commands/generate/phases/contract-addresses-gen.d.ts.map +1 -0
  97. package/dist/types/commands/generate/phases/execution-abi-gen.d.ts +3 -0
  98. package/dist/types/commands/generate/phases/execution-abi-gen.d.ts.map +1 -0
  99. package/dist/types/commands/generate/phases/plugin-actions/index.d.ts +3 -0
  100. package/dist/types/commands/generate/phases/plugin-actions/index.d.ts.map +1 -0
  101. package/dist/types/commands/generate/phases/plugin-actions/management-actions.d.ts +3 -0
  102. package/dist/types/commands/generate/phases/plugin-actions/management-actions.d.ts.map +1 -0
  103. package/dist/types/commands/generate/phases/plugin-actions/read-actions.d.ts +3 -0
  104. package/dist/types/commands/generate/phases/plugin-actions/read-actions.d.ts.map +1 -0
  105. package/dist/types/commands/generate/phases/plugin-generator/get-contract-gen.d.ts +3 -0
  106. package/dist/types/commands/generate/phases/plugin-generator/get-contract-gen.d.ts.map +1 -0
  107. package/dist/types/commands/generate/phases/plugin-generator/index.d.ts +3 -0
  108. package/dist/types/commands/generate/phases/plugin-generator/index.d.ts.map +1 -0
  109. package/dist/types/commands/generate/phases/plugin-generator/meta-gen.d.ts +3 -0
  110. package/dist/types/commands/generate/phases/plugin-generator/meta-gen.d.ts.map +1 -0
  111. package/dist/types/commands/generate/types.d.ts +17 -0
  112. package/dist/types/commands/generate/types.d.ts.map +1 -0
  113. package/dist/types/commands/generate/utils.d.ts +5 -0
  114. package/dist/types/commands/generate/utils.d.ts.map +1 -0
  115. package/dist/types/commands/init.d.ts +8 -0
  116. package/dist/types/commands/init.d.ts.map +1 -0
  117. package/dist/types/config.d.ts +59 -0
  118. package/dist/types/config.d.ts.map +1 -0
  119. package/dist/types/errors.d.ts +15 -0
  120. package/dist/types/errors.d.ts.map +1 -0
  121. package/dist/types/exports/config.d.ts +2 -0
  122. package/dist/types/exports/config.d.ts.map +1 -0
  123. package/dist/types/exports/index.d.ts +5 -0
  124. package/dist/types/exports/index.d.ts.map +1 -0
  125. package/dist/types/exports/types.d.ts +2 -0
  126. package/dist/types/exports/types.d.ts.map +1 -0
  127. package/dist/types/logger.d.ts +7 -0
  128. package/dist/types/logger.d.ts.map +1 -0
  129. package/dist/types/types.d.ts +7 -0
  130. package/dist/types/types.d.ts.map +1 -0
  131. package/dist/types/utils/findConfig.d.ts +15 -0
  132. package/dist/types/utils/findConfig.d.ts.map +1 -0
  133. package/dist/types/utils/format.d.ts +2 -0
  134. package/dist/types/utils/format.d.ts.map +1 -0
  135. package/dist/types/utils/isUsingTypescript.d.ts +2 -0
  136. package/dist/types/utils/isUsingTypescript.d.ts.map +1 -0
  137. package/dist/types/utils/loadEnv.d.ts +5 -0
  138. package/dist/types/utils/loadEnv.d.ts.map +1 -0
  139. package/dist/types/utils/resolveConfig.d.ts +15 -0
  140. package/dist/types/utils/resolveConfig.d.ts.map +1 -0
  141. package/dist/types/version.d.ts +2 -0
  142. package/dist/types/version.d.ts.map +1 -0
  143. package/package.json +83 -0
  144. package/src/IPlugin.ts +396 -0
  145. package/src/cli.ts +44 -0
  146. package/src/commands/generate/index.ts +270 -0
  147. package/src/commands/generate/phases/contract-abi-gen.ts +12 -0
  148. package/src/commands/generate/phases/contract-addresses-gen.ts +18 -0
  149. package/src/commands/generate/phases/execution-abi-gen.ts +20 -0
  150. package/src/commands/generate/phases/plugin-actions/index.ts +151 -0
  151. package/src/commands/generate/phases/plugin-actions/management-actions.ts +141 -0
  152. package/src/commands/generate/phases/plugin-actions/read-actions.ts +97 -0
  153. package/src/commands/generate/phases/plugin-generator/get-contract-gen.ts +30 -0
  154. package/src/commands/generate/phases/plugin-generator/index.ts +27 -0
  155. package/src/commands/generate/phases/plugin-generator/meta-gen.ts +17 -0
  156. package/src/commands/generate/types.ts +18 -0
  157. package/src/commands/generate/utils.ts +26 -0
  158. package/src/commands/init.ts +93 -0
  159. package/src/config.ts +79 -0
  160. package/src/errors.ts +58 -0
  161. package/src/exports/config.ts +1 -0
  162. package/src/exports/index.ts +7 -0
  163. package/src/exports/types.ts +1 -0
  164. package/src/logger.ts +36 -0
  165. package/src/types.ts +11 -0
  166. package/src/utils/findConfig.ts +37 -0
  167. package/src/utils/format.ts +11 -0
  168. package/src/utils/isUsingTypescript.ts +22 -0
  169. package/src/utils/loadEnv.ts +91 -0
  170. package/src/utils/resolveConfig.ts +25 -0
  171. package/src/version.ts +3 -0
@@ -0,0 +1,270 @@
1
+ import { asyncPipe } from "@aa-sdk/core";
2
+ import { kebabCase } from "change-case";
3
+ import dedent from "dedent";
4
+ import { default as fs } from "fs-extra";
5
+ import { basename, dirname, resolve } from "pathe";
6
+ import pc from "picocolors";
7
+ import {
8
+ createPublicClient,
9
+ getContract,
10
+ http,
11
+ type GetContractReturnType,
12
+ type PublicClient,
13
+ } from "viem";
14
+ import z from "zod";
15
+ import type { Config, PluginConfig } from "../../config.js";
16
+ import { IPluginAbi } from "../../IPlugin.js";
17
+ import * as logger from "../../logger.js";
18
+ import { findConfig } from "../../utils/findConfig.js";
19
+ import { format } from "../../utils/format.js";
20
+ import { getIsUsingTypeScript } from "../../utils/isUsingTypescript.js";
21
+ import { resolveConfig } from "../../utils/resolveConfig.js";
22
+ import { ContractAbiGenPhase } from "./phases/contract-abi-gen.js";
23
+ import { ContractAddressesGenPhase } from "./phases/contract-addresses-gen.js";
24
+ import { ExecutionAbiGenPhase } from "./phases/execution-abi-gen.js";
25
+ import { PluginActionsGenPhase } from "./phases/plugin-actions/index.js";
26
+ import { PluginGeneratorPhase } from "./phases/plugin-generator/index.js";
27
+ import type { Phase, PhaseInput } from "./types.js";
28
+
29
+ const GenerateSchema = z.object({
30
+ /** Path to config file */
31
+ config: z.string().optional(),
32
+ /** Directory to search for config file */
33
+ root: z.string().optional(),
34
+ });
35
+
36
+ export type GenerateOptions = z.infer<typeof GenerateSchema>;
37
+
38
+ export async function generate(options: GenerateOptions = {}) {
39
+ try {
40
+ options = await GenerateSchema.parseAsync(options);
41
+ } catch (error) {
42
+ if (error instanceof z.ZodError) throw error;
43
+ throw error;
44
+ }
45
+
46
+ // Get cli config file
47
+ const configPath = await findConfig(options);
48
+ if (!configPath) {
49
+ if (options.config) {
50
+ throw new Error(`Config not found at ${pc.gray(options.config)}`);
51
+ }
52
+
53
+ throw new Error("Config not found");
54
+ }
55
+
56
+ const resolvedConfigs = await resolveConfig({ configPath });
57
+ // TODO: need to use this in the phases below and add support for JS within the generated code
58
+ // To support that we will likely need to:
59
+ // 1. generate a types.ts file instead of appending the types to the top of the generated code
60
+ // 2. in generated plugin.(ts|js) files we will either import the types or use the type reference comment
61
+ // 3. in generated plugin files we need to check if TS when specifying the types of methods
62
+ const isTypeScript = await getIsUsingTypeScript();
63
+ if (!isTypeScript) {
64
+ throw new Error("Only typescript is supported at the moment.");
65
+ }
66
+
67
+ const outNames = new Set<string>();
68
+ const isArrayConfig = Array.isArray(resolvedConfigs);
69
+ const configs = isArrayConfig ? resolvedConfigs : [resolvedConfigs];
70
+
71
+ for (const config of configs) {
72
+ if (isArrayConfig)
73
+ logger.log(`Using config ${pc.gray(basename(configPath))}`);
74
+
75
+ // group configs by out dir
76
+ if (!config.outDir) throw new Error("outDir is required.");
77
+ if (outNames.has(config.outDir))
78
+ throw new Error(`outDir "${config.outDir}" must be unique.`);
79
+ outNames.add(config.outDir);
80
+ const spinner = logger.spinner();
81
+ const pluginConfigs = config.plugins;
82
+
83
+ // Get contracts from config
84
+ spinner.start("Resolving plugin contracts");
85
+ if (!pluginConfigs || !pluginConfigs.length) {
86
+ logger.warn("no plugins found in config");
87
+ spinner.fail();
88
+ return;
89
+ }
90
+
91
+ // TODO: check that plugins are unique
92
+ const plugins = pluginConfigs.map((plugin) => {
93
+ const chain = plugin.chain ?? config.chain;
94
+ const transport = http(plugin.rpcUrl ?? config.rpcUrl);
95
+
96
+ const client = createPublicClient({
97
+ chain,
98
+ transport,
99
+ });
100
+
101
+ if (!(chain.id in plugin.addresses)) {
102
+ spinner.fail();
103
+ throw new Error(
104
+ `contract address missing for the reference chain ${chain.id}`
105
+ );
106
+ }
107
+
108
+ return {
109
+ contract: getContract({
110
+ address: plugin.addresses[chain.id],
111
+ abi: IPluginAbi,
112
+ client,
113
+ }),
114
+ pluginConfig: plugin,
115
+ };
116
+ });
117
+ spinner.succeed();
118
+
119
+ // Generate plugin files
120
+ await Promise.all(
121
+ plugins.map((plugin) =>
122
+ generatePlugin({
123
+ pluginConfig: plugin.pluginConfig,
124
+ contract: plugin.contract,
125
+ config,
126
+ })
127
+ )
128
+ );
129
+ }
130
+ }
131
+
132
+ // Add more phases here if needed
133
+ const phases: Phase[] = [
134
+ ContractAddressesGenPhase,
135
+ PluginGeneratorPhase,
136
+ PluginActionsGenPhase,
137
+ ExecutionAbiGenPhase,
138
+ ContractAbiGenPhase,
139
+ ];
140
+
141
+ const generatePlugin = async ({
142
+ pluginConfig,
143
+ contract,
144
+ config,
145
+ }: {
146
+ pluginConfig: PluginConfig;
147
+ contract: GetContractReturnType<typeof IPluginAbi, PublicClient>;
148
+ config: Config;
149
+ }) => {
150
+ const spinner = logger.spinner();
151
+ spinner.start(`Generating plugin ${pc.gray(pluginConfig.name)}`);
152
+ // Setup plugin generator
153
+ const imports: Map<
154
+ string,
155
+ {
156
+ types: Set<string>;
157
+ members: Set<string>;
158
+ }
159
+ > = new Map();
160
+ const addImport: PhaseInput["addImport"] = (moduleName, member) => {
161
+ if (!imports.has(moduleName)) {
162
+ imports.set(moduleName, {
163
+ types: new Set(),
164
+ members: new Set(),
165
+ });
166
+ }
167
+
168
+ const module = imports.get(moduleName)!;
169
+ if (member.isType) {
170
+ module.types.add(member.name);
171
+ } else {
172
+ module.members.add(member.name);
173
+ }
174
+ };
175
+ const content: string[] = [];
176
+ const types: Map<string, { definition: string; isPublic: boolean }> =
177
+ new Map();
178
+ const addType: PhaseInput["addType"] = (typeName, typeDef, isPublic) => {
179
+ if (types.has(typeName)) {
180
+ throw new Error(`Type ${typeName} already exists`);
181
+ }
182
+
183
+ types.set(typeName, {
184
+ definition: typeDef.replace(";", ""),
185
+ isPublic: isPublic ?? false,
186
+ });
187
+ };
188
+
189
+ // TODO: we need to handle the case where this isn't typescript because right now we generate types in here
190
+ const result = await asyncPipe(...phases)({
191
+ addImport,
192
+ addType,
193
+ content,
194
+ config,
195
+ contract,
196
+ pluginConfig,
197
+ });
198
+
199
+ // Aggregate Result of phase
200
+ const finalContent = dedent`
201
+ ${Array.from(types.entries())
202
+ .map(
203
+ ([name, type]) =>
204
+ `${type.isPublic ? "export" : ""} type ${name} = ${type.definition};`
205
+ )
206
+ .join("\n\n")}
207
+
208
+ ${result.content.join("\n\n")}
209
+ `;
210
+
211
+ const finalImports = Array.from(imports.entries())
212
+ .map(([moduleName, { members, types }]) => {
213
+ return dedent`
214
+ import { ${Array.from(members.values()).join(",")} ${
215
+ members.size > 0 ? "," : ""
216
+ } ${Array.from(types.values())
217
+ .map((x) => `type ${x}`)
218
+ .join(",")} } from "${moduleName}";
219
+ `;
220
+ })
221
+ .join("\n");
222
+ spinner.succeed();
223
+ await writePlugin({
224
+ config,
225
+ content: finalContent,
226
+ imports: finalImports,
227
+ pluginConfig,
228
+ });
229
+ };
230
+
231
+ const writePlugin = async ({
232
+ config,
233
+ content,
234
+ imports,
235
+ pluginConfig,
236
+ }: {
237
+ pluginConfig: PluginConfig;
238
+ config: Config;
239
+ content: string;
240
+ imports: string;
241
+ }) => {
242
+ const isTypeScript = await getIsUsingTypeScript();
243
+
244
+ const pluginRegEx: RegExp = /[pP]lugin/g;
245
+ // TODO: because we are now generating these ourselves, we have a lot more flexibility
246
+ // We could:
247
+ // 1. generate a types.ts file containing the type defs intead of merging them with content
248
+ // 2. generate an extensions.ts file which doesn't get overwritten on each generation
249
+ // 3. generate an index.ts file which correctly exports all of the types + exports the output of extensions.ts
250
+ // so that it's easier to extend and import without conflicts
251
+ const pluginPath = `${config.outDir}/${kebabCase(
252
+ pluginConfig.name.replaceAll(pluginRegEx, "")
253
+ )}/plugin.${isTypeScript ? "ts" : "js"}`;
254
+
255
+ const code = dedent`
256
+ ${imports}
257
+
258
+ ${content}
259
+ `;
260
+
261
+ const spinner = logger.spinner();
262
+ spinner.start(`Writing plugin to ${pc.gray(pluginPath)}`);
263
+ // Format and write output
264
+ const cwd = process.cwd();
265
+ const outPath = resolve(cwd, pluginPath);
266
+ await fs.ensureDir(dirname(outPath));
267
+ const formatted = await format(code);
268
+ await fs.writeFile(outPath, formatted);
269
+ spinner.succeed();
270
+ };
@@ -0,0 +1,12 @@
1
+ import dedent from "dedent";
2
+ import type { Phase } from "../types";
3
+
4
+ export const ContractAbiGenPhase: Phase = async (input) => {
5
+ const { content, pluginConfig } = input;
6
+ content.push(dedent`
7
+ export const ${pluginConfig.name}Abi = ${JSON.stringify(
8
+ pluginConfig.abi
9
+ )} as const;
10
+ `);
11
+ return input;
12
+ };
@@ -0,0 +1,18 @@
1
+ import dedent from "dedent";
2
+ import type { Address } from "viem";
3
+ import type { Phase } from "../types";
4
+
5
+ export const ContractAddressesGenPhase: Phase = async (input) => {
6
+ const { pluginConfig, content, addImport } = input;
7
+
8
+ addImport("viem", { name: "Address", isType: true });
9
+ content.push(dedent`
10
+ const addresses = {${Object.entries(
11
+ pluginConfig.addresses as Record<number, Address>
12
+ ).reduce(
13
+ (prev, [chainId, addr]) => (prev += `${chainId}: "${addr}" as Address, `),
14
+ ""
15
+ )}} as Record<number, Address>;
16
+ `);
17
+ return input;
18
+ };
@@ -0,0 +1,20 @@
1
+ import dedent from "dedent";
2
+ import type { Phase } from "../types";
3
+ import { executionAbiConst, extractExecutionAbi } from "../utils.js";
4
+
5
+ export const ExecutionAbiGenPhase: Phase = async (input) => {
6
+ const { contract, content, pluginConfig } = input;
7
+
8
+ const { executionFunctions } = await contract.read.pluginManifest();
9
+ const executionAbi = extractExecutionAbi(
10
+ executionFunctions,
11
+ pluginConfig.abi
12
+ );
13
+
14
+ content.push(dedent`
15
+ export const ${executionAbiConst(pluginConfig.name)} = ${JSON.stringify(
16
+ executionAbi
17
+ )} as const;
18
+ `);
19
+ return input;
20
+ };
@@ -0,0 +1,151 @@
1
+ import { asyncPipe } from "@aa-sdk/core";
2
+ import { camelCase } from "change-case";
3
+ import dedent from "dedent";
4
+ import type { Phase } from "../../types";
5
+ import { extractExecutionAbi } from "../../utils.js";
6
+ import { ManagementActionsGenPhase } from "./management-actions.js";
7
+ import { AccountReadActionsGenPhase } from "./read-actions.js";
8
+
9
+ export const PluginActionsGenPhase: Phase = async (input) => {
10
+ const { pluginConfig, contract, addImport, addType } = input;
11
+ const { executionFunctions } = await contract.read.pluginManifest();
12
+ const executionAbiConst = `${pluginConfig.name}ExecutionFunctionAbi`;
13
+ const executionAbi = extractExecutionAbi(
14
+ executionFunctions,
15
+ pluginConfig.abi
16
+ );
17
+
18
+ addImport("viem", { name: "EncodeFunctionDataParameters", isType: true });
19
+ addImport("viem", { name: "Transport", isType: true });
20
+ addImport("viem", { name: "Chain", isType: true });
21
+ addImport("viem", { name: "Client", isType: true });
22
+ addImport("@aa-sdk/core", {
23
+ name: "SmartContractAccount",
24
+ isType: true,
25
+ });
26
+ addImport("@aa-sdk/core", {
27
+ name: "GetAccountParameter",
28
+ isType: true,
29
+ });
30
+ addImport("@aa-sdk/core", {
31
+ name: "SendUserOperationResult",
32
+ isType: true,
33
+ });
34
+ addImport("@aa-sdk/core", {
35
+ name: "GetEntryPointFromAccount",
36
+ isType: true,
37
+ });
38
+ addImport("@aa-sdk/core", {
39
+ name: "UserOperationOverridesParameter",
40
+ isType: true,
41
+ });
42
+ addImport("@aa-sdk/core", {
43
+ name: "UserOperationContext",
44
+ isType: true,
45
+ });
46
+ addImport("@aa-sdk/core", { name: "AccountNotFoundError" });
47
+ addImport("@aa-sdk/core", { name: "isSmartAccountClient" });
48
+ addImport("@aa-sdk/core", { name: "IncompatibleClientError" });
49
+ addImport("@aa-sdk/core", { name: "GetContextParameter", isType: true });
50
+
51
+ const providerFunctionDefs: string[] = [];
52
+ const providerFunctions = executionAbi
53
+ .filter((n) => n.stateMutability !== "view")
54
+ .map((n) => {
55
+ const argsParamString =
56
+ n.inputs.length > 0
57
+ ? dedent`{
58
+ args,
59
+ overrides,
60
+ context,
61
+ account = client.account
62
+ }`
63
+ : dedent`{
64
+ overrides,
65
+ context,
66
+ account = client.account
67
+ }`;
68
+ const argsEncodeString = n.inputs.length > 0 ? "args," : "";
69
+
70
+ providerFunctionDefs.push(dedent`
71
+ ${camelCase(
72
+ n.name
73
+ )}: (args: Pick<EncodeFunctionDataParameters<typeof ${executionAbiConst}, "${
74
+ n.name
75
+ }">, "args"> & UserOperationOverridesParameter<TEntryPointVersion> &
76
+ GetAccountParameter<TAccount> & GetContextParameter<TContext>) =>
77
+ Promise<SendUserOperationResult<TEntryPointVersion>>
78
+ `);
79
+ const methodName = camelCase(n.name);
80
+ return dedent`
81
+ ${methodName}(${argsParamString}) {
82
+ if (!account) {
83
+ throw new AccountNotFoundError();
84
+ }
85
+ if (!isSmartAccountClient(client)) {
86
+ throw new IncompatibleClientError("SmartAccountClient", "${methodName}", client);
87
+ }
88
+
89
+ const uo = encodeFunctionData({
90
+ abi: ${executionAbiConst},
91
+ functionName: "${n.name}",
92
+ ${argsEncodeString}
93
+ });
94
+
95
+ return client.sendUserOperation({ uo, overrides, account, context });
96
+ }
97
+ `;
98
+ });
99
+
100
+ addType(
101
+ `ExecutionActions<
102
+ TAccount extends SmartContractAccount | undefined = SmartContractAccount | undefined,
103
+ TContext extends UserOperationContext | undefined = UserOperationContext | undefined,
104
+ TEntryPointVersion extends GetEntryPointFromAccount<TAccount> = GetEntryPointFromAccount<TAccount>
105
+ >`,
106
+ dedent`{
107
+ ${providerFunctionDefs.join(";\n\n")}
108
+ }`
109
+ );
110
+
111
+ const { hasReadMethods } = await asyncPipe(
112
+ ManagementActionsGenPhase,
113
+ AccountReadActionsGenPhase
114
+ )({
115
+ ...input,
116
+ content: providerFunctions,
117
+ });
118
+
119
+ addType(
120
+ `${pluginConfig.name}Actions<
121
+ TAccount extends SmartContractAccount | undefined =
122
+ | SmartContractAccount
123
+ | undefined,
124
+ TContext extends UserOperationContext | undefined = UserOperationContext | undefined>`,
125
+ dedent`
126
+ ExecutionActions<TAccount, TContext> & ManagementActions<TAccount, TContext> & ReadAndEncodeActions${
127
+ hasReadMethods ? "<TAccount>" : ""
128
+ }
129
+ `,
130
+ true
131
+ );
132
+
133
+ input.content.push(dedent`
134
+ export const ${camelCase(pluginConfig.name)}Actions: <
135
+ TTransport extends Transport = Transport,
136
+ TChain extends Chain | undefined = Chain | undefined,
137
+ TAccount extends SmartContractAccount | undefined =
138
+ | SmartContractAccount
139
+ | undefined,
140
+ TContext extends UserOperationContext | undefined = UserOperationContext | undefined
141
+ >(
142
+ client: Client<TTransport, TChain, TAccount>
143
+ ) => ${
144
+ pluginConfig.name
145
+ }Actions<TAccount, TContext> = (client) => ({ ${providerFunctions.join(
146
+ ",\n"
147
+ )} });
148
+ `);
149
+
150
+ return input;
151
+ };
@@ -0,0 +1,141 @@
1
+ import { kebabCase } from "change-case";
2
+ import dedent from "dedent";
3
+ import type { PluginConfig } from "../../../../config";
4
+ import type { Phase, PhaseInput } from "../../types";
5
+
6
+ export const ManagementActionsGenPhase: Phase = async (input) => {
7
+ const { addImport, pluginConfig, addType } = input;
8
+ if (pluginConfig.installConfig != null) {
9
+ addImports(
10
+ addImport,
11
+ pluginConfig.installConfig.dependencies?.map((x) => x.plugin) ?? []
12
+ );
13
+
14
+ const initArgs = pluginConfig.installConfig.initAbiParams ?? [];
15
+
16
+ addType("InstallArgs", JSON.stringify(initArgs));
17
+ addType(
18
+ `Install${pluginConfig.name}Params`,
19
+ dedent`{
20
+ args: Parameters<typeof encodeAbiParameters<InstallArgs>>[1];
21
+ pluginAddress?: Address;
22
+ dependencyOverrides?: FunctionReference[];
23
+ }`,
24
+ true
25
+ );
26
+ addType(
27
+ `ManagementActions<
28
+ TAccount extends SmartContractAccount | undefined = SmartContractAccount | undefined,
29
+ TContext extends UserOperationContext | undefined = Record<string,any> | undefined,
30
+ TEntryPointVersion extends GetEntryPointFromAccount<TAccount> = GetEntryPointFromAccount<TAccount>
31
+ >`,
32
+ dedent`{
33
+ install${pluginConfig.name}: (args:
34
+ UserOperationOverridesParameter<TEntryPointVersion> &
35
+ Install${pluginConfig.name}Params & GetAccountParameter<TAccount> & GetContextParameter<TContext>) =>
36
+ Promise<SendUserOperationResult<TEntryPointVersion>>
37
+ }`
38
+ );
39
+
40
+ const dependencies = (pluginConfig.installConfig.dependencies ?? []).map(
41
+ (x) => dedent`
42
+ (() => {
43
+ const pluginAddress = ${x.plugin.name}.meta.addresses[chain.id];
44
+ if (!pluginAddress) {
45
+ throw new Error("missing ${x.plugin.name} address for chain " + chain.name);
46
+ }
47
+
48
+ return encodePacked(
49
+ ["address", "uint8"],
50
+ [pluginAddress, ${x.functionId}]
51
+ );
52
+ })()
53
+ `
54
+ );
55
+
56
+ const installMethodName = `install${pluginConfig.name}`;
57
+
58
+ input.content.push(dedent`
59
+ ${installMethodName}({account = client.account, overrides, context, ...params}) {
60
+ if (!account) {
61
+ throw new AccountNotFoundError();
62
+ }
63
+
64
+ if (!isSmartAccountClient(client)) {
65
+ throw new IncompatibleClientError("SmartAccountClient", "${installMethodName}", client);
66
+ }
67
+
68
+ const chain = client.chain;
69
+ if (!chain) {
70
+ throw new ChainNotFoundError();
71
+ }
72
+
73
+ const dependencies = params.dependencyOverrides ?? [${dependencies.join(
74
+ ",\n\n"
75
+ )}];
76
+ const pluginAddress = params.pluginAddress ?? ${
77
+ pluginConfig.name
78
+ }.meta.addresses[chain.id] as Address | undefined;
79
+
80
+ if (!pluginAddress) {
81
+ throw new Error("missing ${
82
+ pluginConfig.name
83
+ } address for chain " + chain.name);
84
+ }
85
+
86
+ return installPlugin_(client, {
87
+ pluginAddress,
88
+ pluginInitData: encodeAbiParameters(${JSON.stringify(
89
+ initArgs
90
+ )}, params.args),
91
+ dependencies,
92
+ overrides,
93
+ account,
94
+ context,
95
+ });
96
+ }
97
+ `);
98
+ }
99
+
100
+ return input;
101
+ };
102
+
103
+ const addImports = (
104
+ addImport: PhaseInput["addImport"],
105
+ deps?: PluginConfig[]
106
+ ) => {
107
+ if (deps != null && deps.length > 0) {
108
+ addImport("viem", { name: "encodePacked" });
109
+ deps.forEach((x) => {
110
+ addImport(
111
+ `../${kebabCase(x.name.replaceAll(/[pP]lugin/g, ""))}/plugin.js`,
112
+ {
113
+ name: x.name,
114
+ }
115
+ );
116
+ });
117
+ }
118
+
119
+ addImport("@aa-sdk/core", { name: "ChainNotFoundError" });
120
+ addImport("viem", { name: "encodeAbiParameters" });
121
+ addImport("@account-kit/smart-contracts", {
122
+ name: "installPlugin as installPlugin_",
123
+ });
124
+ addImport("@aa-sdk/core", {
125
+ name: "GetAccountParameter",
126
+ isType: true,
127
+ });
128
+ addImport("@aa-sdk/core", {
129
+ name: "GetEntryPointFromAccount",
130
+ isType: true,
131
+ });
132
+ addImport("@aa-sdk/core", {
133
+ name: "UserOperationContext",
134
+ isType: true,
135
+ });
136
+ addImport("@account-kit/smart-contracts", {
137
+ name: "FunctionReference",
138
+ isType: true,
139
+ });
140
+ addImport("@aa-sdk/core", { name: "GetContextParameter", isType: true });
141
+ };