solforge 0.2.4 → 0.2.6

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 (82) hide show
  1. package/README.md +471 -79
  2. package/cli.cjs +106 -78
  3. package/package.json +1 -1
  4. package/scripts/install.sh +1 -1
  5. package/scripts/postinstall.cjs +69 -61
  6. package/server/lib/base58.ts +1 -1
  7. package/server/methods/account/get-account-info.ts +3 -7
  8. package/server/methods/account/get-balance.ts +3 -7
  9. package/server/methods/account/get-multiple-accounts.ts +2 -1
  10. package/server/methods/account/get-parsed-account-info.ts +3 -7
  11. package/server/methods/account/parsers/index.ts +2 -2
  12. package/server/methods/account/parsers/loader-upgradeable.ts +14 -1
  13. package/server/methods/account/parsers/spl-token.ts +29 -10
  14. package/server/methods/account/request-airdrop.ts +44 -31
  15. package/server/methods/block/get-block.ts +3 -7
  16. package/server/methods/block/get-blocks-with-limit.ts +3 -7
  17. package/server/methods/block/is-blockhash-valid.ts +3 -7
  18. package/server/methods/get-address-lookup-table.ts +3 -7
  19. package/server/methods/program/get-program-accounts.ts +9 -9
  20. package/server/methods/program/get-token-account-balance.ts +3 -7
  21. package/server/methods/program/get-token-accounts-by-delegate.ts +4 -3
  22. package/server/methods/program/get-token-accounts-by-owner.ts +61 -35
  23. package/server/methods/program/get-token-largest-accounts.ts +3 -2
  24. package/server/methods/program/get-token-supply.ts +3 -2
  25. package/server/methods/solforge/index.ts +9 -6
  26. package/server/methods/transaction/get-parsed-transaction.ts +3 -7
  27. package/server/methods/transaction/get-signature-statuses.ts +14 -7
  28. package/server/methods/transaction/get-signatures-for-address.ts +3 -7
  29. package/server/methods/transaction/get-transaction.ts +167 -81
  30. package/server/methods/transaction/send-transaction.ts +29 -16
  31. package/server/methods/transaction/simulate-transaction.ts +3 -2
  32. package/server/rpc-server.ts +47 -34
  33. package/server/types.ts +9 -6
  34. package/server/ws-server.ts +15 -8
  35. package/src/api-server-entry.ts +91 -91
  36. package/src/cli/commands/airdrop.ts +2 -2
  37. package/src/cli/commands/config.ts +2 -2
  38. package/src/cli/commands/mint.ts +3 -3
  39. package/src/cli/commands/program-clone.ts +9 -11
  40. package/src/cli/commands/program-load.ts +3 -3
  41. package/src/cli/commands/rpc-start.ts +8 -5
  42. package/src/cli/commands/token-adopt-authority.ts +1 -1
  43. package/src/cli/commands/token-clone.ts +5 -6
  44. package/src/cli/commands/token-create.ts +5 -5
  45. package/src/cli/main.ts +38 -37
  46. package/src/cli/run-solforge.ts +20 -6
  47. package/src/cli/setup-wizard.ts +8 -6
  48. package/src/commands/add-program.ts +324 -328
  49. package/src/commands/init.ts +106 -106
  50. package/src/commands/list.ts +125 -125
  51. package/src/commands/mint.ts +247 -248
  52. package/src/commands/start.ts +837 -833
  53. package/src/commands/status.ts +80 -80
  54. package/src/commands/stop.ts +381 -382
  55. package/src/config/index.ts +33 -17
  56. package/src/config/manager.ts +150 -150
  57. package/src/db/index.ts +2 -2
  58. package/src/db/tx-store.ts +12 -8
  59. package/src/gui/public/app.css +1556 -1
  60. package/src/gui/public/build/main.css +1569 -1
  61. package/src/gui/server.ts +21 -22
  62. package/src/gui/src/api.ts +1 -1
  63. package/src/gui/src/app.tsx +96 -45
  64. package/src/gui/src/components/airdrop-mint-form.tsx +49 -19
  65. package/src/gui/src/components/clone-program-modal.tsx +31 -12
  66. package/src/gui/src/components/clone-token-modal.tsx +32 -13
  67. package/src/gui/src/components/modal.tsx +18 -11
  68. package/src/gui/src/components/programs-panel.tsx +27 -15
  69. package/src/gui/src/components/status-panel.tsx +32 -18
  70. package/src/gui/src/components/tokens-panel.tsx +25 -19
  71. package/src/gui/src/index.css +491 -463
  72. package/src/index.ts +177 -149
  73. package/src/rpc/start.ts +1 -1
  74. package/src/services/api-server.ts +494 -475
  75. package/src/services/port-manager.ts +164 -167
  76. package/src/services/process-registry.ts +144 -145
  77. package/src/services/program-cloner.ts +312 -312
  78. package/src/services/token-cloner.ts +799 -797
  79. package/src/services/validator.ts +288 -290
  80. package/src/types/config.ts +72 -72
  81. package/src/utils/shell.ts +75 -75
  82. package/src/utils/token-loader.ts +78 -78
@@ -2,61 +2,61 @@ import { z } from "zod";
2
2
 
3
3
  // Token configuration schema
4
4
  export const TokenConfigSchema = z.object({
5
- symbol: z.string().min(1, "Token symbol is required"),
6
- mainnetMint: z.string().min(1, "Mainnet mint address is required"),
7
- mintAuthority: z.string().optional(), // Path to keypair file
8
- mintAmount: z
9
- .number()
10
- .positive("Mint amount must be positive")
11
- .default(1000000), // Amount to mint to mint authority
12
- recipients: z
13
- .array(
14
- z.object({
15
- wallet: z.string().min(1, "Wallet address is required"),
16
- amount: z.number().positive("Amount must be positive"),
17
- })
18
- )
19
- .default([]),
20
- cloneMetadata: z.boolean().default(true), // Whether to clone token metadata
5
+ symbol: z.string().min(1, "Token symbol is required"),
6
+ mainnetMint: z.string().min(1, "Mainnet mint address is required"),
7
+ mintAuthority: z.string().optional(), // Path to keypair file
8
+ mintAmount: z
9
+ .number()
10
+ .positive("Mint amount must be positive")
11
+ .default(1000000), // Amount to mint to mint authority
12
+ recipients: z
13
+ .array(
14
+ z.object({
15
+ wallet: z.string().min(1, "Wallet address is required"),
16
+ amount: z.number().positive("Amount must be positive"),
17
+ }),
18
+ )
19
+ .default([]),
20
+ cloneMetadata: z.boolean().default(true), // Whether to clone token metadata
21
21
  });
22
22
 
23
23
  // Program configuration schema
24
24
  export const ProgramConfigSchema = z.object({
25
- name: z.string().optional(),
26
- mainnetProgramId: z.string().min(1, "Program ID is required"),
27
- deployPath: z.string().optional(), // Optional path to local .so file
28
- upgradeable: z.boolean().default(false),
29
- cluster: z
30
- .enum(["mainnet-beta", "devnet", "testnet"])
31
- .default("mainnet-beta"),
32
- dependencies: z.array(z.string()).default([]), // Other program IDs this program depends on
25
+ name: z.string().optional(),
26
+ mainnetProgramId: z.string().min(1, "Program ID is required"),
27
+ deployPath: z.string().optional(), // Optional path to local .so file
28
+ upgradeable: z.boolean().default(false),
29
+ cluster: z
30
+ .enum(["mainnet-beta", "devnet", "testnet"])
31
+ .default("mainnet-beta"),
32
+ dependencies: z.array(z.string()).default([]), // Other program IDs this program depends on
33
33
  });
34
34
 
35
35
  // Localnet configuration schema
36
36
  export const LocalnetConfigSchema = z.object({
37
- airdropAmount: z.number().positive().default(100),
38
- faucetAccounts: z.array(z.string()).default([]),
39
- logLevel: z.enum(["trace", "debug", "info", "warn", "error"]).default("info"),
40
- port: z.number().int().min(1000).max(65535).default(8899),
41
- faucetPort: z.number().int().min(1000).max(65535).default(9900),
42
- reset: z.boolean().default(false),
43
- quiet: z.boolean().default(false),
44
- ledgerPath: z.string().optional(),
45
- bindAddress: z.string().default("127.0.0.1"),
46
- limitLedgerSize: z.number().int().positive().default(100000),
47
- rpc: z
48
- .string()
49
- .url("RPC must be a valid URL")
50
- .default("https://api.mainnet-beta.solana.com"),
37
+ airdropAmount: z.number().positive().default(100),
38
+ faucetAccounts: z.array(z.string()).default([]),
39
+ logLevel: z.enum(["trace", "debug", "info", "warn", "error"]).default("info"),
40
+ port: z.number().int().min(1000).max(65535).default(8899),
41
+ faucetPort: z.number().int().min(1000).max(65535).default(9900),
42
+ reset: z.boolean().default(false),
43
+ quiet: z.boolean().default(false),
44
+ ledgerPath: z.string().optional(),
45
+ bindAddress: z.string().default("127.0.0.1"),
46
+ limitLedgerSize: z.number().int().positive().default(100000),
47
+ rpc: z
48
+ .string()
49
+ .url("RPC must be a valid URL")
50
+ .default("https://api.mainnet-beta.solana.com"),
51
51
  });
52
52
 
53
53
  // Complete configuration schema
54
54
  export const ConfigSchema = z.object({
55
- name: z.string().default("solforge-localnet"),
56
- description: z.string().optional(),
57
- tokens: z.array(TokenConfigSchema).default([]),
58
- programs: z.array(ProgramConfigSchema).default([]),
59
- localnet: LocalnetConfigSchema.default({}),
55
+ name: z.string().default("solforge-localnet"),
56
+ description: z.string().optional(),
57
+ tokens: z.array(TokenConfigSchema).default([]),
58
+ programs: z.array(ProgramConfigSchema).default([]),
59
+ localnet: LocalnetConfigSchema.default({}),
60
60
  });
61
61
 
62
62
  // Inferred TypeScript types
@@ -67,44 +67,44 @@ export type Config = z.infer<typeof ConfigSchema>;
67
67
 
68
68
  // Validator status types
69
69
  export type ValidatorStatus =
70
- | "stopped"
71
- | "starting"
72
- | "running"
73
- | "stopping"
74
- | "error";
70
+ | "stopped"
71
+ | "starting"
72
+ | "running"
73
+ | "stopping"
74
+ | "error";
75
75
 
76
76
  export interface ValidatorState {
77
- status: ValidatorStatus;
78
- pid?: number;
79
- port: number;
80
- faucetPort: number;
81
- rpcUrl: string;
82
- wsUrl: string;
83
- startTime?: Date;
84
- logs: string[];
85
- error?: string;
77
+ status: ValidatorStatus;
78
+ pid?: number;
79
+ port: number;
80
+ faucetPort: number;
81
+ rpcUrl: string;
82
+ wsUrl: string;
83
+ startTime?: Date;
84
+ logs: string[];
85
+ error?: string;
86
86
  }
87
87
 
88
88
  // Operation result types
89
- export interface OperationResult<T = any> {
90
- success: boolean;
91
- data?: T;
92
- error?: string;
93
- details?: any;
89
+ export interface OperationResult<T = unknown> {
90
+ success: boolean;
91
+ data?: T;
92
+ error?: string;
93
+ details?: unknown;
94
94
  }
95
95
 
96
96
  export interface CloneResult {
97
- type: "program" | "token";
98
- id: string;
99
- name?: string;
100
- success: boolean;
101
- error?: string;
97
+ type: "program" | "token";
98
+ id: string;
99
+ name?: string;
100
+ success: boolean;
101
+ error?: string;
102
102
  }
103
103
 
104
104
  export interface ValidationResult {
105
- valid: boolean;
106
- errors: Array<{
107
- path: string;
108
- message: string;
109
- }>;
105
+ valid: boolean;
106
+ errors: Array<{
107
+ path: string;
108
+ message: string;
109
+ }>;
110
110
  }
@@ -2,109 +2,109 @@ import { $ } from "bun";
2
2
  import chalk from "chalk";
3
3
 
4
4
  export interface CommandResult {
5
- success: boolean;
6
- stdout: string;
7
- stderr: string;
8
- exitCode: number;
5
+ success: boolean;
6
+ stdout: string;
7
+ stderr: string;
8
+ exitCode: number;
9
9
  }
10
10
 
11
11
  /**
12
12
  * Execute a shell command and return the result
13
13
  */
14
14
  export async function runCommand(
15
- command: string,
16
- args: string[] = [],
17
- options: {
18
- silent?: boolean;
19
- jsonOutput?: boolean;
20
- debug?: boolean;
21
- } = {}
15
+ command: string,
16
+ args: string[] = [],
17
+ options: {
18
+ silent?: boolean;
19
+ jsonOutput?: boolean;
20
+ debug?: boolean;
21
+ } = {},
22
22
  ): Promise<CommandResult> {
23
- const { silent = false, jsonOutput = false, debug = false } = options;
23
+ const { silent = false, jsonOutput = false, debug = false } = options;
24
24
 
25
- try {
26
- if (!silent || debug) {
27
- console.log(chalk.gray(`$ ${command} ${args.join(" ")}`));
28
- }
25
+ try {
26
+ if (!silent || debug) {
27
+ console.log(chalk.gray(`$ ${command} ${args.join(" ")}`));
28
+ }
29
29
 
30
- const result = await $`${command} ${args}`.quiet();
30
+ const result = await $`${command} ${args}`.quiet();
31
31
 
32
- const stdout = result.stdout.toString();
33
- const stderr = result.stderr.toString();
34
- const exitCode = result.exitCode;
35
- const success = exitCode === 0;
32
+ const stdout = result.stdout.toString();
33
+ const stderr = result.stderr.toString();
34
+ const exitCode = result.exitCode;
35
+ const success = exitCode === 0;
36
36
 
37
- if (debug) {
38
- console.log(chalk.gray(`Exit code: ${exitCode}`));
39
- if (stdout) {
40
- console.log(chalk.gray(`Stdout: ${stdout}`));
41
- }
42
- if (stderr) {
43
- console.log(chalk.gray(`Stderr: ${stderr}`));
44
- }
45
- }
37
+ if (debug) {
38
+ console.log(chalk.gray(`Exit code: ${exitCode}`));
39
+ if (stdout) {
40
+ console.log(chalk.gray(`Stdout: ${stdout}`));
41
+ }
42
+ if (stderr) {
43
+ console.log(chalk.gray(`Stderr: ${stderr}`));
44
+ }
45
+ }
46
46
 
47
- if (!silent && !success) {
48
- console.error(chalk.red(`Command failed with exit code ${exitCode}`));
49
- if (stderr) {
50
- console.error(chalk.red(`Error: ${stderr}`));
51
- }
52
- }
47
+ if (!silent && !success) {
48
+ console.error(chalk.red(`Command failed with exit code ${exitCode}`));
49
+ if (stderr) {
50
+ console.error(chalk.red(`Error: ${stderr}`));
51
+ }
52
+ }
53
53
 
54
- // If JSON output is expected, try to parse it
55
- let parsedOutput = stdout;
56
- if (jsonOutput && success && stdout.trim()) {
57
- try {
58
- parsedOutput = JSON.parse(stdout);
59
- } catch (e) {
60
- // If JSON parsing fails, keep original stdout
61
- console.warn(
62
- chalk.yellow("Warning: Expected JSON output but got invalid JSON")
63
- );
64
- }
65
- }
54
+ // If JSON output is expected, try to parse it
55
+ let parsedOutput = stdout;
56
+ if (jsonOutput && success && stdout.trim()) {
57
+ try {
58
+ parsedOutput = JSON.parse(stdout);
59
+ } catch (_e) {
60
+ // If JSON parsing fails, keep original stdout
61
+ console.warn(
62
+ chalk.yellow("Warning: Expected JSON output but got invalid JSON"),
63
+ );
64
+ }
65
+ }
66
66
 
67
- return {
68
- success,
69
- stdout: parsedOutput,
70
- stderr,
71
- exitCode,
72
- };
73
- } catch (error) {
74
- const errorMessage = error instanceof Error ? error.message : String(error);
67
+ return {
68
+ success,
69
+ stdout: parsedOutput,
70
+ stderr,
71
+ exitCode,
72
+ };
73
+ } catch (error) {
74
+ const errorMessage = error instanceof Error ? error.message : String(error);
75
75
 
76
- if (!silent) {
77
- console.error(chalk.red(`Command execution failed: ${errorMessage}`));
78
- }
76
+ if (!silent) {
77
+ console.error(chalk.red(`Command execution failed: ${errorMessage}`));
78
+ }
79
79
 
80
- return {
81
- success: false,
82
- stdout: "",
83
- stderr: errorMessage,
84
- exitCode: 1,
85
- };
86
- }
80
+ return {
81
+ success: false,
82
+ stdout: "",
83
+ stderr: errorMessage,
84
+ exitCode: 1,
85
+ };
86
+ }
87
87
  }
88
88
 
89
89
  /**
90
90
  * Check if a command exists in PATH
91
91
  */
92
92
  export async function commandExists(command: string): Promise<boolean> {
93
- const result = await runCommand("which", [command], { silent: true });
94
- return result.success;
93
+ const result = await runCommand("which", [command], { silent: true });
94
+ return result.success;
95
95
  }
96
96
 
97
97
  /**
98
98
  * Check if solana CLI tools are available
99
99
  */
100
100
  export async function checkSolanaTools(): Promise<{
101
- solana: boolean;
102
- splToken: boolean;
101
+ solana: boolean;
102
+ splToken: boolean;
103
103
  }> {
104
- const [solana, splToken] = await Promise.all([
105
- commandExists("solana"),
106
- commandExists("spl-token"),
107
- ]);
104
+ const [solana, splToken] = await Promise.all([
105
+ commandExists("solana"),
106
+ commandExists("spl-token"),
107
+ ]);
108
108
 
109
- return { solana, splToken };
109
+ return { solana, splToken };
110
110
  }
@@ -1,17 +1,17 @@
1
- import { existsSync, readFileSync } from "fs";
2
- import { join } from "path";
3
1
  import { Keypair } from "@solana/web3.js";
2
+ import { existsSync, readFileSync } from "node:fs";
3
+ import { join } from "node:path";
4
4
  import type { TokenConfig } from "../types/config.js";
5
5
 
6
6
  export interface ClonedToken {
7
- config: TokenConfig;
8
- mintAuthorityPath: string;
9
- modifiedAccountPath: string;
10
- metadataAccountPath?: string;
11
- mintAuthority: {
12
- publicKey: string;
13
- secretKey: number[];
14
- };
7
+ config: TokenConfig;
8
+ mintAuthorityPath: string;
9
+ modifiedAccountPath: string;
10
+ metadataAccountPath?: string;
11
+ mintAuthority: {
12
+ publicKey: string;
13
+ secretKey: number[];
14
+ };
15
15
  }
16
16
 
17
17
  /**
@@ -19,97 +19,97 @@ export interface ClonedToken {
19
19
  * This ensures consistent token loading across CLI and API server.
20
20
  */
21
21
  export async function loadClonedTokens(
22
- tokenConfigs: TokenConfig[],
23
- workDir: string = ".solforge"
22
+ tokenConfigs: TokenConfig[],
23
+ workDir: string = ".solforge",
24
24
  ): Promise<ClonedToken[]> {
25
- const clonedTokens: ClonedToken[] = [];
25
+ const clonedTokens: ClonedToken[] = [];
26
26
 
27
- // Load shared mint authority
28
- const sharedMintAuthorityPath = join(workDir, "shared-mint-authority.json");
29
- let sharedMintAuthority: { publicKey: string; secretKey: number[] } | null =
30
- null;
27
+ // Load shared mint authority
28
+ const sharedMintAuthorityPath = join(workDir, "shared-mint-authority.json");
29
+ let sharedMintAuthority: { publicKey: string; secretKey: number[] } | null =
30
+ null;
31
31
 
32
- if (existsSync(sharedMintAuthorityPath)) {
33
- try {
34
- const fileContent = JSON.parse(
35
- readFileSync(sharedMintAuthorityPath, "utf8")
36
- );
32
+ if (existsSync(sharedMintAuthorityPath)) {
33
+ try {
34
+ const fileContent = JSON.parse(
35
+ readFileSync(sharedMintAuthorityPath, "utf8"),
36
+ );
37
37
 
38
- if (Array.isArray(fileContent)) {
39
- // New format: file contains just the secret key array
40
- const keypair = Keypair.fromSecretKey(new Uint8Array(fileContent));
41
- sharedMintAuthority = {
42
- publicKey: keypair.publicKey.toBase58(),
43
- secretKey: Array.from(keypair.secretKey),
44
- };
38
+ if (Array.isArray(fileContent)) {
39
+ // New format: file contains just the secret key array
40
+ const keypair = Keypair.fromSecretKey(new Uint8Array(fileContent));
41
+ sharedMintAuthority = {
42
+ publicKey: keypair.publicKey.toBase58(),
43
+ secretKey: Array.from(keypair.secretKey),
44
+ };
45
45
 
46
- // Check metadata for consistency
47
- const metadataPath = join(workDir, "shared-mint-authority-meta.json");
48
- if (existsSync(metadataPath)) {
49
- const metadata = JSON.parse(readFileSync(metadataPath, "utf8"));
50
- if (metadata.publicKey !== sharedMintAuthority.publicKey) {
51
- sharedMintAuthority.publicKey = metadata.publicKey;
52
- }
53
- }
54
- } else {
55
- // Old format: file contains {publicKey, secretKey}
56
- sharedMintAuthority = fileContent;
57
- }
58
- } catch (error) {
59
- console.error("Failed to load shared mint authority:", error);
60
- return [];
61
- }
62
- }
46
+ // Check metadata for consistency
47
+ const metadataPath = join(workDir, "shared-mint-authority-meta.json");
48
+ if (existsSync(metadataPath)) {
49
+ const metadata = JSON.parse(readFileSync(metadataPath, "utf8"));
50
+ if (metadata.publicKey !== sharedMintAuthority.publicKey) {
51
+ sharedMintAuthority.publicKey = metadata.publicKey;
52
+ }
53
+ }
54
+ } else {
55
+ // Old format: file contains {publicKey, secretKey}
56
+ sharedMintAuthority = fileContent;
57
+ }
58
+ } catch (error) {
59
+ console.error("Failed to load shared mint authority:", error);
60
+ return [];
61
+ }
62
+ }
63
63
 
64
- if (!sharedMintAuthority) {
65
- return [];
66
- }
64
+ if (!sharedMintAuthority) {
65
+ return [];
66
+ }
67
67
 
68
- // Load each token that has been cloned
69
- for (const tokenConfig of tokenConfigs) {
70
- const tokenDir = join(workDir, `token-${tokenConfig.symbol.toLowerCase()}`);
71
- const modifiedAccountPath = join(tokenDir, "modified.json");
72
- const metadataAccountPath = join(tokenDir, "metadata.json");
68
+ // Load each token that has been cloned
69
+ for (const tokenConfig of tokenConfigs) {
70
+ const tokenDir = join(workDir, `token-${tokenConfig.symbol.toLowerCase()}`);
71
+ const modifiedAccountPath = join(tokenDir, "modified.json");
72
+ const metadataAccountPath = join(tokenDir, "metadata.json");
73
73
 
74
- // Check if this token has already been cloned
75
- if (existsSync(modifiedAccountPath)) {
76
- const clonedToken: ClonedToken = {
77
- config: tokenConfig,
78
- mintAuthorityPath: sharedMintAuthorityPath,
79
- modifiedAccountPath,
80
- mintAuthority: sharedMintAuthority,
81
- };
74
+ // Check if this token has already been cloned
75
+ if (existsSync(modifiedAccountPath)) {
76
+ const clonedToken: ClonedToken = {
77
+ config: tokenConfig,
78
+ mintAuthorityPath: sharedMintAuthorityPath,
79
+ modifiedAccountPath,
80
+ mintAuthority: sharedMintAuthority,
81
+ };
82
82
 
83
- // Add metadata path if it exists
84
- if (existsSync(metadataAccountPath)) {
85
- clonedToken.metadataAccountPath = metadataAccountPath;
86
- }
83
+ // Add metadata path if it exists
84
+ if (existsSync(metadataAccountPath)) {
85
+ clonedToken.metadataAccountPath = metadataAccountPath;
86
+ }
87
87
 
88
- clonedTokens.push(clonedToken);
89
- }
90
- }
88
+ clonedTokens.push(clonedToken);
89
+ }
90
+ }
91
91
 
92
- return clonedTokens;
92
+ return clonedTokens;
93
93
  }
94
94
 
95
95
  /**
96
96
  * Find a cloned token by its mint address
97
97
  */
98
98
  export function findTokenByMint(
99
- clonedTokens: ClonedToken[],
100
- mintAddress: string
99
+ clonedTokens: ClonedToken[],
100
+ mintAddress: string,
101
101
  ): ClonedToken | undefined {
102
- return clonedTokens.find((token) => token.config.mainnetMint === mintAddress);
102
+ return clonedTokens.find((token) => token.config.mainnetMint === mintAddress);
103
103
  }
104
104
 
105
105
  /**
106
106
  * Find a cloned token by its symbol
107
107
  */
108
108
  export function findTokenBySymbol(
109
- clonedTokens: ClonedToken[],
110
- symbol: string
109
+ clonedTokens: ClonedToken[],
110
+ symbol: string,
111
111
  ): ClonedToken | undefined {
112
- return clonedTokens.find(
113
- (token) => token.config.symbol.toLowerCase() === symbol.toLowerCase()
114
- );
112
+ return clonedTokens.find(
113
+ (token) => token.config.symbol.toLowerCase() === symbol.toLowerCase(),
114
+ );
115
115
  }