@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,97 @@
1
+ import { pascalCase } from "change-case";
2
+ import dedent from "dedent";
3
+ import type { Phase } from "../../types";
4
+ import { extractExecutionAbi } from "../../utils.js";
5
+
6
+ export const AccountReadActionsGenPhase: Phase = async (input) => {
7
+ const { pluginConfig, contract, addImport, addType } = input;
8
+ const { executionFunctions } = await contract.read.pluginManifest();
9
+ const executionAbiConst = `${pluginConfig.name}ExecutionFunctionAbi`;
10
+ const executionAbi = extractExecutionAbi(
11
+ executionFunctions,
12
+ pluginConfig.abi
13
+ );
14
+
15
+ addImport("viem", { name: "EncodeFunctionDataParameters", isType: true });
16
+ addImport("viem", { name: "encodeFunctionData" });
17
+ addImport("viem", { name: "Hex", isType: true });
18
+
19
+ const accountFunctionActionDefs: string[] = [];
20
+
21
+ const accountFunctions = executionAbi.map((n) => {
22
+ const methodContent: string[] = [];
23
+ const argsParamString = n.inputs.length > 0 ? `{ args }` : "";
24
+ const argsEncodeString = n.inputs.length > 0 ? "args," : "";
25
+ const isViewFunction = n.stateMutability === "view";
26
+
27
+ const encodeMethodName = `encode${pascalCase(n.name)}`;
28
+ accountFunctionActionDefs.push(
29
+ dedent`${encodeMethodName}: (args: Pick<EncodeFunctionDataParameters<typeof ${executionAbiConst}, "${n.name}">, "args">) => Hex`
30
+ );
31
+ methodContent.push(dedent`
32
+ ${encodeMethodName}(${argsParamString}) {
33
+ return encodeFunctionData({
34
+ abi: ${executionAbiConst},
35
+ functionName: "${n.name}",
36
+ ${argsEncodeString}
37
+ });
38
+ }
39
+ `);
40
+
41
+ const readArgsParamString =
42
+ n.inputs.length > 0
43
+ ? `{ args, account = client.account }`
44
+ : "{ account = client.account }";
45
+ if (isViewFunction) {
46
+ addImport("viem", { name: "ReadContractReturnType", isType: true });
47
+ input.hasReadMethods = true;
48
+ const readMethodName = `read${pascalCase(n.name)}`;
49
+ accountFunctionActionDefs.push(
50
+ n.inputs.length > 0
51
+ ? dedent`${readMethodName}: (
52
+ args: Pick<EncodeFunctionDataParameters<typeof ${executionAbiConst}, "${n.name}">, "args"> &
53
+ GetAccountParameter<TAccount>
54
+ ) => Promise<ReadContractReturnType<typeof ${executionAbiConst}, "${n.name}">>`
55
+ : dedent`${readMethodName}: (args: GetAccountParameter<TAccount>) =>
56
+ Promise<ReadContractReturnType<typeof ${executionAbiConst}, "${n.name}">>`
57
+ );
58
+
59
+ methodContent.push(dedent`
60
+ async ${readMethodName} (${readArgsParamString}) {
61
+ if (!account) {
62
+ throw new AccountNotFoundError();
63
+ }
64
+
65
+ if (!isSmartAccountClient(client)) {
66
+ throw new IncompatibleClientError("SmartAccountClient", "${readMethodName}", client);
67
+ }
68
+
69
+ return client.readContract({
70
+ address: account.address,
71
+ abi: ${executionAbiConst},
72
+ functionName: "${n.name}",
73
+ ${argsEncodeString}
74
+ });
75
+ }
76
+ `);
77
+ }
78
+
79
+ return methodContent.join(",\n\n");
80
+ });
81
+
82
+ const typeName = input.hasReadMethods
83
+ ? `ReadAndEncodeActions<
84
+ TAccount extends SmartContractAccount | undefined = SmartContractAccount | undefined,
85
+ >`
86
+ : "ReadAndEncodeActions";
87
+
88
+ addType(
89
+ typeName,
90
+ dedent`{
91
+ ${accountFunctionActionDefs.join(";\n\n")}
92
+ }`
93
+ );
94
+ input.content.push(...accountFunctions);
95
+
96
+ return input;
97
+ };
@@ -0,0 +1,30 @@
1
+ import dedent from "dedent";
2
+ import type { Phase } from "../../types";
3
+
4
+ export const GetContractGenPhase: Phase = async (input) => {
5
+ const { content, pluginConfig, addImport } = input;
6
+
7
+ addImport("viem", { name: "getContract", isType: false });
8
+ addImport("viem", { name: "GetContractReturnType", isType: true });
9
+ addImport("viem", { name: "Address", isType: true });
10
+ addImport("viem", { name: "Transport", isType: true });
11
+ addImport("viem", { name: "PublicClient", isType: true });
12
+ addImport("viem", { name: "Client", isType: true });
13
+ addImport("@aa-sdk/core", { name: "ChainNotFoundError" });
14
+
15
+ content.push(dedent`
16
+ getContract: <C extends Client>(
17
+ client: C,
18
+ address?: Address
19
+ ): GetContractReturnType<typeof ${pluginConfig.name}Abi, PublicClient, Address> => {
20
+ if (!client.chain) throw new ChainNotFoundError();
21
+
22
+ return getContract({
23
+ address: address || addresses[client.chain.id],
24
+ abi: ${pluginConfig.name}Abi,
25
+ client: client,
26
+ });
27
+ }`);
28
+
29
+ return input;
30
+ };
@@ -0,0 +1,27 @@
1
+ import { asyncPipe } from "@aa-sdk/core";
2
+ import dedent from "dedent";
3
+ import type { Phase } from "../../types";
4
+ import { GetContractGenPhase } from "./get-contract-gen.js";
5
+ import { MetaGenPhase } from "./meta-gen.js";
6
+
7
+ export const PluginGeneratorPhase: Phase = async (input) => {
8
+ const pluginPhases: Phase[] = [MetaGenPhase, GetContractGenPhase];
9
+ const { pluginConfig, addImport } = input;
10
+
11
+ const result = await asyncPipe(...pluginPhases)({
12
+ ...input,
13
+ content: [],
14
+ });
15
+
16
+ addImport("@account-kit/smart-contracts", { name: "Plugin", isType: true });
17
+
18
+ input.content.push(dedent`
19
+ export const ${pluginConfig.name}: Plugin<typeof ${
20
+ pluginConfig.name
21
+ }Abi> = {
22
+ ${result.content.join(",\n")}
23
+ };
24
+ `);
25
+
26
+ return input;
27
+ };
@@ -0,0 +1,17 @@
1
+ import dedent from "dedent";
2
+ import type { Phase } from "../../types";
3
+
4
+ export const MetaGenPhase: Phase = async (input) => {
5
+ const { contract, content } = input;
6
+ const { name, version } = await contract.read.pluginMetadata();
7
+
8
+ content.push(dedent`
9
+ meta: {
10
+ name: "${name}",
11
+ version: "${version}",
12
+ addresses,
13
+ }
14
+ `);
15
+
16
+ return input;
17
+ };
@@ -0,0 +1,18 @@
1
+ import type { GetContractReturnType, PublicClient } from "viem";
2
+ import type { Config, PluginConfig } from "../../config.js";
3
+ import type { IPluginAbi } from "../../IPlugin.js";
4
+
5
+ export type PhaseInput = {
6
+ content: string[];
7
+ addImport: (
8
+ moduleName: string,
9
+ member: { name: string; isType?: boolean }
10
+ ) => void;
11
+ addType: (typeName: string, typeDef: string, isPublic?: boolean) => void;
12
+ pluginConfig: PluginConfig;
13
+ config: Config;
14
+ contract: GetContractReturnType<typeof IPluginAbi, PublicClient>;
15
+ hasReadMethods?: boolean;
16
+ };
17
+
18
+ export type Phase = (input: PhaseInput) => Promise<PhaseInput>;
@@ -0,0 +1,26 @@
1
+ import { type AbiFunction } from "abitype";
2
+ import { getAbiItem, type Abi, type AbiItem, type Address } from "viem";
3
+
4
+ export const executionAbiConst = (name: string) =>
5
+ `${name}ExecutionFunctionAbi`;
6
+
7
+ export const extractExecutionAbi = (
8
+ executionFunctions: readonly Address[],
9
+ abi: Abi
10
+ ): AbiFunction[] => {
11
+ return executionFunctions.map((f) => {
12
+ const item = getAbiItem({
13
+ abi: abi,
14
+ name: f,
15
+ }) as AbiItem;
16
+
17
+ if (item.type !== "function") {
18
+ throw new Error(
19
+ "execution function not mapping to a function in the ABI",
20
+ { cause: JSON.stringify(item, null, 2) }
21
+ );
22
+ }
23
+
24
+ return item;
25
+ });
26
+ };
@@ -0,0 +1,93 @@
1
+ // borrows heavily from https://github.com/wevm/wagmi/blob/main/packages/cli/src/commands/init.ts
2
+ import dedent from "dedent";
3
+ import { default as fs } from "fs-extra";
4
+ import { relative, resolve } from "pathe";
5
+ import pc from "picocolors";
6
+ import z from "zod";
7
+ import { fromZodError } from "../errors.js";
8
+ import * as logger from "../logger.js";
9
+ import { findConfig } from "../utils/findConfig.js";
10
+ import { format } from "../utils/format.js";
11
+ import { getIsUsingTypeScript } from "../utils/isUsingTypescript.js";
12
+
13
+ export type InitOptions = {
14
+ /** Path to config file */
15
+ config?: string;
16
+ /** Directory to init config file */
17
+ root?: string;
18
+ };
19
+
20
+ const InitSchema = z.object({
21
+ config: z.string().optional(),
22
+ root: z.string().optional(),
23
+ });
24
+
25
+ export async function init(options: InitOptions = {}) {
26
+ try {
27
+ options = await InitSchema.parseAsync(options);
28
+ } catch (error) {
29
+ if (error instanceof z.ZodError)
30
+ throw fromZodError(error, { prefix: "Invalid option" });
31
+ throw error;
32
+ }
33
+
34
+ // Check for existing config file
35
+ const configPath = await findConfig(options);
36
+ if (configPath) {
37
+ logger.info(
38
+ `Config already exists at ${pc.gray(relative(process.cwd(), configPath))}`
39
+ );
40
+ return configPath;
41
+ }
42
+
43
+ const spinner = logger.spinner();
44
+ spinner.start("Creating config");
45
+ // Check if project is using TypeScript
46
+ const isUsingTypeScript = await getIsUsingTypeScript();
47
+ const rootDir = resolve(options.root || process.cwd());
48
+ let outPath: string;
49
+ if (options.config) {
50
+ outPath = resolve(rootDir, options.config);
51
+ } else {
52
+ const extension = isUsingTypeScript ? "ts" : "js";
53
+ outPath = resolve(rootDir, `plugingen.config.${extension}`);
54
+ }
55
+
56
+ let content: string;
57
+ if (isUsingTypeScript) {
58
+ content = dedent(`
59
+ import { defineConfig } from '@account-kit/plugingen';
60
+ import { sepolia } from "viem/chains";
61
+
62
+ export default defineConfig({
63
+ outDir: "./src/generated",
64
+ chain: sepolia,
65
+ rpcUrl: "https://ethereum-sepolia.publicnode.com",
66
+ plugins: [],
67
+ });
68
+ `);
69
+ } else {
70
+ content = dedent(`
71
+ // @ts-check
72
+
73
+ /** @type {import('@account-kit/plugingen').Config} */
74
+ export default ${dedent`
75
+ {
76
+ outDir: "./src/generated",
77
+ chain: sepolia,
78
+ rpcUrl: "https://ethereum-sepolia.publicnode.com",
79
+ plugins: [],
80
+ }
81
+ `.replace(/"(\d*)":/gm, "$1:")}
82
+ `);
83
+ }
84
+
85
+ const formatted = await format(content);
86
+ await fs.writeFile(outPath, formatted);
87
+ spinner.succeed();
88
+ logger.success(
89
+ `Config created at ${pc.gray(relative(process.cwd(), outPath))}`
90
+ );
91
+
92
+ return outPath;
93
+ }
package/src/config.ts ADDED
@@ -0,0 +1,79 @@
1
+ import type { Abi, Address, Chain, Hex, parseAbiParameters } from "viem";
2
+ import type { Evaluate, MaybeArray, MaybePromise } from "./types";
3
+
4
+ export type ContractConfig<
5
+ chainId extends number = number,
6
+ requiredChainId extends number | undefined = undefined
7
+ > = {
8
+ /**
9
+ * Contract ABI
10
+ */
11
+ abi: Abi;
12
+ /**
13
+ * Contract address or addresses.
14
+ *
15
+ * Accepts an object `{ [chainId]: address }` to support multiple chains.
16
+ *
17
+ * @example
18
+ * '0x314159265dd8dbb310642f98f50c066173c1259b'
19
+ *
20
+ * @example
21
+ * {
22
+ * 1: '0x314159265dd8dbb310642f98f50c066173c1259b',
23
+ * 5: '0x112234455c3a32fd11230c42e7bccd4a84e02010',
24
+ * }
25
+ */
26
+ address?:
27
+ | Address
28
+ | (requiredChainId extends number
29
+ ? Record<requiredChainId, Address> & Partial<Record<chainId, Address>>
30
+ : Record<chainId, Address>)
31
+ | undefined;
32
+ /**
33
+ * Name of contract.
34
+ */
35
+ name: string;
36
+ };
37
+
38
+ export type Contract = Evaluate<
39
+ ContractConfig & {
40
+ /** Generated string content */
41
+ content: string;
42
+ /** Meta info about contract */
43
+ meta: {
44
+ abiName: string;
45
+ addressName?: string | undefined;
46
+ configName?: string | undefined;
47
+ };
48
+ }
49
+ >;
50
+
51
+ export type PluginConfig = {
52
+ abi: Abi;
53
+ addresses: Record<number, Address>;
54
+ // allows you to override the root config chain
55
+ chain?: Chain;
56
+ // allows you to override the root config rpcUrl
57
+ rpcUrl?: string;
58
+ name: string;
59
+ installConfig?: {
60
+ initAbiParams: ReturnType<typeof parseAbiParameters> | [];
61
+ dependencies?: {
62
+ plugin: PluginConfig;
63
+ functionId: Hex;
64
+ }[];
65
+ };
66
+ };
67
+
68
+ export type Config = {
69
+ chain: Chain;
70
+ rpcUrl: string;
71
+ outDir?: string;
72
+ plugins: PluginConfig[];
73
+ };
74
+
75
+ export function defineConfig(
76
+ config: MaybeArray<Config> | (() => MaybePromise<MaybeArray<Config>>)
77
+ ) {
78
+ return config;
79
+ }
package/src/errors.ts ADDED
@@ -0,0 +1,58 @@
1
+ import { type z } from "zod";
2
+
3
+ // from https://github.com/wevm/wagmi/blob/main/packages/cli/src/errors.ts
4
+ class ValidationError extends Error {
5
+ details: Zod.ZodIssue[];
6
+
7
+ constructor(
8
+ message: string,
9
+ options: {
10
+ details: Zod.ZodIssue[];
11
+ }
12
+ ) {
13
+ super(message);
14
+ this.details = options.details;
15
+ }
16
+ }
17
+
18
+ // From https://github.com/causaly/zod-validation-error
19
+ export function fromZodError(
20
+ zError: z.ZodError,
21
+ {
22
+ maxIssuesInMessage = 99,
23
+ issueSeparator = "\n- ",
24
+ prefixSeparator = "\n- ",
25
+ prefix = "Validation Error",
26
+ }: {
27
+ maxIssuesInMessage?: number;
28
+ issueSeparator?: string;
29
+ prefixSeparator?: string;
30
+ prefix?: string;
31
+ } = {}
32
+ ): ValidationError {
33
+ function joinPath(arr: Array<string | number>): string {
34
+ return arr.reduce<string>((acc, value) => {
35
+ if (typeof value === "number") return `${acc}[${value}]`;
36
+ const separator = acc === "" ? "" : ".";
37
+ return acc + separator + value;
38
+ }, "");
39
+ }
40
+
41
+ const reason = zError.errors
42
+ // limit max number of issues printed in the reason section
43
+ .slice(0, maxIssuesInMessage)
44
+ // format error message
45
+ .map((issue) => {
46
+ const { message, path } = issue;
47
+ if (path.length > 0) return `${message} at \`${joinPath(path)}\``;
48
+ return message;
49
+ })
50
+ // concat as string
51
+ .join(issueSeparator);
52
+
53
+ const message = reason ? [prefix, reason].join(prefixSeparator) : prefix;
54
+
55
+ return new ValidationError(message, {
56
+ details: zError.errors,
57
+ });
58
+ }
@@ -0,0 +1 @@
1
+ export { defineConfig, type Config, type PluginConfig } from "../config.js";
@@ -0,0 +1,7 @@
1
+ export { defineConfig, type Config, type PluginConfig } from "../config.js";
2
+
3
+ export * as logger from "../logger.js";
4
+
5
+ export { loadEnv } from "../utils/loadEnv.js";
6
+
7
+ export { VERSION } from "../version.js";
@@ -0,0 +1 @@
1
+ export { IPluginAbi } from "../IPlugin.js";
package/src/logger.ts ADDED
@@ -0,0 +1,36 @@
1
+ import ora from "ora";
2
+ import pc from "picocolors";
3
+ import { format as utilFormat } from "util";
4
+
5
+ function format(args: any[]) {
6
+ return utilFormat(...args)
7
+ .split("\n")
8
+ .join("\n");
9
+ }
10
+
11
+ export function success(...args: any[]) {
12
+ console.log(pc.green(format(args)));
13
+ }
14
+
15
+ export function info(...args: any[]) {
16
+ console.info(pc.blue(format(args)));
17
+ }
18
+
19
+ export function log(...args: any[]) {
20
+ console.log(pc.white(format(args)));
21
+ }
22
+
23
+ export function warn(...args: any[]) {
24
+ console.warn(pc.yellow(format(args)));
25
+ }
26
+
27
+ export function error(...args: any[]) {
28
+ console.error(pc.red(format(args)));
29
+ }
30
+
31
+ export function spinner() {
32
+ return ora({
33
+ color: "yellow",
34
+ spinner: "dots",
35
+ });
36
+ }
package/src/types.ts ADDED
@@ -0,0 +1,11 @@
1
+ // Source: https://github.com/wevm/wagmi/blob/main/packages/cli/src/types.ts
2
+ export type Evaluate<type> = { [key in keyof type]: type[key] } & unknown;
3
+
4
+ export type MaybeArray<T> = T | T[];
5
+
6
+ export type MaybePromise<T> = T | Promise<T>;
7
+
8
+ export type RequiredBy<TType, TKeys extends keyof TType> = Required<
9
+ Pick<TType, TKeys>
10
+ > &
11
+ Omit<TType, TKeys>;
@@ -0,0 +1,37 @@
1
+ // source: https://github.com/wevm/wagmi/blob/main/packages/cli/src/utils/findConfig.ts
2
+ import { findUp } from "find-up";
3
+ import { default as fs } from "fs-extra";
4
+ import { resolve } from "pathe";
5
+
6
+ // Do not reorder
7
+ // In order of preference files are checked
8
+ const configFiles = [
9
+ "plugingen.config.ts",
10
+ "plugingen.config.js",
11
+ "plugingen.config.mjs",
12
+ "plugingen.config.mts",
13
+ ];
14
+
15
+ type FindConfigParameters = {
16
+ /** Config file name */
17
+ config?: string;
18
+ /** Config file directory */
19
+ root?: string;
20
+ };
21
+
22
+ /**
23
+ * Resolves path to plugingen CLI config file.
24
+ *
25
+ * @param parameters - optional override parameters for finding the config object
26
+ * @returns the path to the config file
27
+ */
28
+ export async function findConfig(parameters: FindConfigParameters = {}) {
29
+ const { config, root } = parameters;
30
+ const rootDir = resolve(root || process.cwd());
31
+ if (config) {
32
+ const path = resolve(rootDir, config);
33
+ if (fs.pathExistsSync(path)) return path;
34
+ return;
35
+ }
36
+ return findUp(configFiles, { cwd: rootDir });
37
+ }
@@ -0,0 +1,11 @@
1
+ // https://github.com/wevm/wagmi/blob/main/packages/cli/src/utils/format.ts
2
+ import prettier from "prettier";
3
+
4
+ export async function format(content: string) {
5
+ const config = await prettier.resolveConfig(process.cwd());
6
+ return prettier.format(content, {
7
+ parser: "typescript",
8
+ ...config,
9
+ plugins: [],
10
+ });
11
+ }
@@ -0,0 +1,22 @@
1
+ // https://github.com/wevm/wagmi/blob/main/packages/cli/src/utils/getIsUsingTypeScript.ts
2
+ import { findUp } from "find-up";
3
+
4
+ export async function getIsUsingTypeScript() {
5
+ try {
6
+ const cwd = process.cwd();
7
+ const tsconfig = await findUp("tsconfig.json", { cwd });
8
+ if (tsconfig) return true;
9
+
10
+ const plugingenConfig = await findUp(
11
+ ["plugingenConfig.config.ts", "plugingenConfig.config.mts"],
12
+ {
13
+ cwd,
14
+ }
15
+ );
16
+ if (plugingenConfig) return true;
17
+
18
+ return false;
19
+ } catch {
20
+ return false;
21
+ }
22
+ }
@@ -0,0 +1,91 @@
1
+ // https://github.com/wevm/wagmi/blob/main/packages/cli/src/utils/loadEnv.ts
2
+ import { parse } from "dotenv";
3
+ import { expand } from "dotenv-expand";
4
+
5
+ import { existsSync, readFileSync, statSync } from "node:fs";
6
+ import { dirname, join } from "node:path";
7
+
8
+ // https://github.com/vitejs/vite/blob/main/packages/vite/src/node/env.ts#L7
9
+ export function loadEnv(
10
+ config: {
11
+ mode?: string;
12
+ envDir?: string;
13
+ } = {}
14
+ ): Record<string, string> {
15
+ const mode = config.mode;
16
+ if (mode === "local") {
17
+ throw new Error(
18
+ `"local" cannot be used as a mode name because it conflicts with the .local postfix for .env files.`
19
+ );
20
+ }
21
+
22
+ const envFiles = [
23
+ /** default file */ ".env",
24
+ /** local file */ ".env.local",
25
+ ...(mode
26
+ ? [
27
+ /** mode file */ `.env.${mode}`,
28
+ /** mode local file */ `.env.${mode}.local`,
29
+ ]
30
+ : []),
31
+ ];
32
+
33
+ const envDir = config.envDir ?? process.cwd();
34
+ const parsed = Object.fromEntries(
35
+ envFiles.flatMap((file) => {
36
+ const path = lookupFile(envDir, [file], {
37
+ pathOnly: true,
38
+ rootDir: envDir,
39
+ });
40
+ if (!path) return [];
41
+ return Object.entries(parse(readFileSync(path)));
42
+ })
43
+ );
44
+
45
+ try {
46
+ // let environment variables use each other
47
+ expand({ parsed });
48
+ } catch (error) {
49
+ // custom error handling until https://github.com/motdotla/dotenv-expand/issues/65 is fixed upstream
50
+ // check for message "TypeError: Cannot read properties of undefined (reading 'split')"
51
+ if ((error as Error).message.includes("split")) {
52
+ throw new Error(
53
+ "dotenv-expand failed to expand env vars. Maybe you need to escape `$`?"
54
+ );
55
+ }
56
+ throw error;
57
+ }
58
+
59
+ return parsed;
60
+ }
61
+
62
+ function lookupFile(
63
+ dir: string,
64
+ formats: string[],
65
+ options?: {
66
+ pathOnly?: boolean;
67
+ rootDir?: string;
68
+ predicate?: (file: string) => boolean;
69
+ }
70
+ ): string | undefined {
71
+ for (const format of formats) {
72
+ const fullPath = join(dir, format);
73
+ if (existsSync(fullPath) && statSync(fullPath).isFile()) {
74
+ const result = options?.pathOnly
75
+ ? fullPath
76
+ : readFileSync(fullPath, "utf-8");
77
+ if (!options?.predicate || options.predicate(result)) {
78
+ return result;
79
+ }
80
+ }
81
+ }
82
+
83
+ const parentDir = dirname(dir);
84
+ if (
85
+ parentDir !== dir &&
86
+ (!options?.rootDir || parentDir.startsWith(options?.rootDir))
87
+ )
88
+ return lookupFile(parentDir, formats, options);
89
+
90
+ return undefined;
91
+ }