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.
- package/README.md +471 -79
- package/cli.cjs +106 -78
- package/package.json +1 -1
- package/scripts/install.sh +1 -1
- package/scripts/postinstall.cjs +69 -61
- package/server/lib/base58.ts +1 -1
- package/server/methods/account/get-account-info.ts +3 -7
- package/server/methods/account/get-balance.ts +3 -7
- package/server/methods/account/get-multiple-accounts.ts +2 -1
- package/server/methods/account/get-parsed-account-info.ts +3 -7
- package/server/methods/account/parsers/index.ts +2 -2
- package/server/methods/account/parsers/loader-upgradeable.ts +14 -1
- package/server/methods/account/parsers/spl-token.ts +29 -10
- package/server/methods/account/request-airdrop.ts +44 -31
- package/server/methods/block/get-block.ts +3 -7
- package/server/methods/block/get-blocks-with-limit.ts +3 -7
- package/server/methods/block/is-blockhash-valid.ts +3 -7
- package/server/methods/get-address-lookup-table.ts +3 -7
- package/server/methods/program/get-program-accounts.ts +9 -9
- package/server/methods/program/get-token-account-balance.ts +3 -7
- package/server/methods/program/get-token-accounts-by-delegate.ts +4 -3
- package/server/methods/program/get-token-accounts-by-owner.ts +61 -35
- package/server/methods/program/get-token-largest-accounts.ts +3 -2
- package/server/methods/program/get-token-supply.ts +3 -2
- package/server/methods/solforge/index.ts +9 -6
- package/server/methods/transaction/get-parsed-transaction.ts +3 -7
- package/server/methods/transaction/get-signature-statuses.ts +14 -7
- package/server/methods/transaction/get-signatures-for-address.ts +3 -7
- package/server/methods/transaction/get-transaction.ts +167 -81
- package/server/methods/transaction/send-transaction.ts +29 -16
- package/server/methods/transaction/simulate-transaction.ts +3 -2
- package/server/rpc-server.ts +47 -34
- package/server/types.ts +9 -6
- package/server/ws-server.ts +15 -8
- package/src/api-server-entry.ts +91 -91
- package/src/cli/commands/airdrop.ts +2 -2
- package/src/cli/commands/config.ts +2 -2
- package/src/cli/commands/mint.ts +3 -3
- package/src/cli/commands/program-clone.ts +9 -11
- package/src/cli/commands/program-load.ts +3 -3
- package/src/cli/commands/rpc-start.ts +8 -5
- package/src/cli/commands/token-adopt-authority.ts +1 -1
- package/src/cli/commands/token-clone.ts +5 -6
- package/src/cli/commands/token-create.ts +5 -5
- package/src/cli/main.ts +38 -37
- package/src/cli/run-solforge.ts +20 -6
- package/src/cli/setup-wizard.ts +8 -6
- package/src/commands/add-program.ts +324 -328
- package/src/commands/init.ts +106 -106
- package/src/commands/list.ts +125 -125
- package/src/commands/mint.ts +247 -248
- package/src/commands/start.ts +837 -833
- package/src/commands/status.ts +80 -80
- package/src/commands/stop.ts +381 -382
- package/src/config/index.ts +33 -17
- package/src/config/manager.ts +150 -150
- package/src/db/index.ts +2 -2
- package/src/db/tx-store.ts +12 -8
- package/src/gui/public/app.css +1556 -1
- package/src/gui/public/build/main.css +1569 -1
- package/src/gui/server.ts +21 -22
- package/src/gui/src/api.ts +1 -1
- package/src/gui/src/app.tsx +96 -45
- package/src/gui/src/components/airdrop-mint-form.tsx +49 -19
- package/src/gui/src/components/clone-program-modal.tsx +31 -12
- package/src/gui/src/components/clone-token-modal.tsx +32 -13
- package/src/gui/src/components/modal.tsx +18 -11
- package/src/gui/src/components/programs-panel.tsx +27 -15
- package/src/gui/src/components/status-panel.tsx +32 -18
- package/src/gui/src/components/tokens-panel.tsx +25 -19
- package/src/gui/src/index.css +491 -463
- package/src/index.ts +177 -149
- package/src/rpc/start.ts +1 -1
- package/src/services/api-server.ts +494 -475
- package/src/services/port-manager.ts +164 -167
- package/src/services/process-registry.ts +144 -145
- package/src/services/program-cloner.ts +312 -312
- package/src/services/token-cloner.ts +799 -797
- package/src/services/validator.ts +288 -290
- package/src/types/config.ts +72 -72
- package/src/utils/shell.ts +75 -75
- package/src/utils/token-loader.ts +78 -78
package/src/types/config.ts
CHANGED
|
@@ -2,61 +2,61 @@ import { z } from "zod";
|
|
|
2
2
|
|
|
3
3
|
// Token configuration schema
|
|
4
4
|
export const TokenConfigSchema = z.object({
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
70
|
+
| "stopped"
|
|
71
|
+
| "starting"
|
|
72
|
+
| "running"
|
|
73
|
+
| "stopping"
|
|
74
|
+
| "error";
|
|
75
75
|
|
|
76
76
|
export interface ValidatorState {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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 =
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
105
|
+
valid: boolean;
|
|
106
|
+
errors: Array<{
|
|
107
|
+
path: string;
|
|
108
|
+
message: string;
|
|
109
|
+
}>;
|
|
110
110
|
}
|
package/src/utils/shell.ts
CHANGED
|
@@ -2,109 +2,109 @@ import { $ } from "bun";
|
|
|
2
2
|
import chalk from "chalk";
|
|
3
3
|
|
|
4
4
|
export interface CommandResult {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
command: string,
|
|
16
|
+
args: string[] = [],
|
|
17
|
+
options: {
|
|
18
|
+
silent?: boolean;
|
|
19
|
+
jsonOutput?: boolean;
|
|
20
|
+
debug?: boolean;
|
|
21
|
+
} = {},
|
|
22
22
|
): Promise<CommandResult> {
|
|
23
|
-
|
|
23
|
+
const { silent = false, jsonOutput = false, debug = false } = options;
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
try {
|
|
26
|
+
if (!silent || debug) {
|
|
27
|
+
console.log(chalk.gray(`$ ${command} ${args.join(" ")}`));
|
|
28
|
+
}
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
const result = await $`${command} ${args}`.quiet();
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
-
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
if (!silent) {
|
|
77
|
+
console.error(chalk.red(`Command execution failed: ${errorMessage}`));
|
|
78
|
+
}
|
|
79
79
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
-
|
|
94
|
-
|
|
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
|
-
|
|
102
|
-
|
|
101
|
+
solana: boolean;
|
|
102
|
+
splToken: boolean;
|
|
103
103
|
}> {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
104
|
+
const [solana, splToken] = await Promise.all([
|
|
105
|
+
commandExists("solana"),
|
|
106
|
+
commandExists("spl-token"),
|
|
107
|
+
]);
|
|
108
108
|
|
|
109
|
-
|
|
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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
|
|
23
|
-
|
|
22
|
+
tokenConfigs: TokenConfig[],
|
|
23
|
+
workDir: string = ".solforge",
|
|
24
24
|
): Promise<ClonedToken[]> {
|
|
25
|
-
|
|
25
|
+
const clonedTokens: ClonedToken[] = [];
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
if (existsSync(sharedMintAuthorityPath)) {
|
|
33
|
+
try {
|
|
34
|
+
const fileContent = JSON.parse(
|
|
35
|
+
readFileSync(sharedMintAuthorityPath, "utf8"),
|
|
36
|
+
);
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
if (!sharedMintAuthority) {
|
|
65
|
+
return [];
|
|
66
|
+
}
|
|
67
67
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
83
|
+
// Add metadata path if it exists
|
|
84
|
+
if (existsSync(metadataAccountPath)) {
|
|
85
|
+
clonedToken.metadataAccountPath = metadataAccountPath;
|
|
86
|
+
}
|
|
87
87
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
88
|
+
clonedTokens.push(clonedToken);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
91
|
|
|
92
|
-
|
|
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
|
-
|
|
100
|
-
|
|
99
|
+
clonedTokens: ClonedToken[],
|
|
100
|
+
mintAddress: string,
|
|
101
101
|
): ClonedToken | undefined {
|
|
102
|
-
|
|
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
|
-
|
|
110
|
-
|
|
109
|
+
clonedTokens: ClonedToken[],
|
|
110
|
+
symbol: string,
|
|
111
111
|
): ClonedToken | undefined {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
112
|
+
return clonedTokens.find(
|
|
113
|
+
(token) => token.config.symbol.toLowerCase() === symbol.toLowerCase(),
|
|
114
|
+
);
|
|
115
115
|
}
|