solforge 0.1.6 → 0.1.7
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 +1 -1
- package/package.json +1 -1
- package/src/commands/init.ts +2 -2
- package/src/commands/mint.ts +9 -57
- package/src/commands/start.ts +1 -2
- package/src/services/api-server.ts +11 -58
- package/src/utils/token-loader.ts +115 -0
package/README.md
CHANGED
package/package.json
CHANGED
package/src/commands/init.ts
CHANGED
|
@@ -22,11 +22,11 @@ const defaultConfig: Config = {
|
|
|
22
22
|
faucetAccounts: [],
|
|
23
23
|
port: 8899,
|
|
24
24
|
faucetPort: 9900,
|
|
25
|
-
reset:
|
|
25
|
+
reset: false,
|
|
26
26
|
logLevel: "info",
|
|
27
27
|
bindAddress: "127.0.0.1",
|
|
28
28
|
quiet: false,
|
|
29
|
-
rpc: "https://mainnet
|
|
29
|
+
rpc: "https://api.mainnet-beta.solana.com",
|
|
30
30
|
limitLedgerSize: 100000,
|
|
31
31
|
},
|
|
32
32
|
};
|
package/src/commands/mint.ts
CHANGED
|
@@ -5,22 +5,12 @@ import { join } from "path";
|
|
|
5
5
|
import { input, select } from "@inquirer/prompts";
|
|
6
6
|
import { runCommand } from "../utils/shell";
|
|
7
7
|
import { Keypair, PublicKey } from "@solana/web3.js";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
interface ClonedToken {
|
|
16
|
-
config: TokenConfig;
|
|
17
|
-
mintAuthorityPath: string;
|
|
18
|
-
modifiedAccountPath: string;
|
|
19
|
-
mintAuthority: {
|
|
20
|
-
publicKey: string;
|
|
21
|
-
secretKey: number[];
|
|
22
|
-
};
|
|
23
|
-
}
|
|
8
|
+
import {
|
|
9
|
+
loadClonedTokens,
|
|
10
|
+
findTokenBySymbol,
|
|
11
|
+
type ClonedToken,
|
|
12
|
+
} from "../utils/token-loader.js";
|
|
13
|
+
import type { TokenConfig } from "../types/config.js";
|
|
24
14
|
|
|
25
15
|
export const mintCommand = new Command()
|
|
26
16
|
.name("mint")
|
|
@@ -71,9 +61,7 @@ export const mintCommand = new Command()
|
|
|
71
61
|
// Select token (or use provided symbol)
|
|
72
62
|
let selectedToken: ClonedToken;
|
|
73
63
|
if (options.symbol) {
|
|
74
|
-
const token = tokens.
|
|
75
|
-
(t) => t.config.symbol.toLowerCase() === options.symbol.toLowerCase()
|
|
76
|
-
);
|
|
64
|
+
const token = findTokenBySymbol(tokens, options.symbol);
|
|
77
65
|
if (!token) {
|
|
78
66
|
console.error(
|
|
79
67
|
chalk.red(`❌ Token ${options.symbol} not found in cloned tokens`)
|
|
@@ -181,8 +169,6 @@ export const mintCommand = new Command()
|
|
|
181
169
|
});
|
|
182
170
|
|
|
183
171
|
async function loadAvailableTokens(workDir: string): Promise<ClonedToken[]> {
|
|
184
|
-
const tokens: ClonedToken[] = [];
|
|
185
|
-
|
|
186
172
|
try {
|
|
187
173
|
// Load token config from sf.config.json
|
|
188
174
|
const configPath = "sf.config.json";
|
|
@@ -193,42 +179,8 @@ async function loadAvailableTokens(workDir: string): Promise<ClonedToken[]> {
|
|
|
193
179
|
const config = JSON.parse(readFileSync(configPath, "utf8"));
|
|
194
180
|
const tokenConfigs: TokenConfig[] = config.tokens || [];
|
|
195
181
|
|
|
196
|
-
//
|
|
197
|
-
|
|
198
|
-
if (!existsSync(sharedMintAuthorityPath)) {
|
|
199
|
-
throw new Error("Shared mint authority not found");
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
const secretKeyArray = JSON.parse(
|
|
203
|
-
readFileSync(sharedMintAuthorityPath, "utf8")
|
|
204
|
-
);
|
|
205
|
-
const mintAuthorityKeypair = Keypair.fromSecretKey(
|
|
206
|
-
new Uint8Array(secretKeyArray)
|
|
207
|
-
);
|
|
208
|
-
const mintAuthority = {
|
|
209
|
-
publicKey: mintAuthorityKeypair.publicKey.toBase58(),
|
|
210
|
-
secretKey: Array.from(mintAuthorityKeypair.secretKey),
|
|
211
|
-
};
|
|
212
|
-
|
|
213
|
-
// Build cloned tokens list
|
|
214
|
-
for (const tokenConfig of tokenConfigs) {
|
|
215
|
-
const tokenDir = join(
|
|
216
|
-
workDir,
|
|
217
|
-
`token-${tokenConfig.symbol.toLowerCase()}`
|
|
218
|
-
);
|
|
219
|
-
const modifiedAccountPath = join(tokenDir, "modified.json");
|
|
220
|
-
|
|
221
|
-
if (existsSync(modifiedAccountPath)) {
|
|
222
|
-
tokens.push({
|
|
223
|
-
config: tokenConfig,
|
|
224
|
-
mintAuthorityPath: sharedMintAuthorityPath,
|
|
225
|
-
modifiedAccountPath,
|
|
226
|
-
mintAuthority,
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
return tokens;
|
|
182
|
+
// Use the shared token loader
|
|
183
|
+
return await loadClonedTokens(tokenConfigs, workDir);
|
|
232
184
|
} catch (error) {
|
|
233
185
|
throw new Error(`Failed to load tokens: ${error}`);
|
|
234
186
|
}
|
package/src/commands/start.ts
CHANGED
|
@@ -6,11 +6,10 @@ import { join } from "path";
|
|
|
6
6
|
import { runCommand, checkSolanaTools } from "../utils/shell.js";
|
|
7
7
|
import { configManager } from "../config/manager.js";
|
|
8
8
|
import { TokenCloner } from "../services/token-cloner.js";
|
|
9
|
-
import { ProgramCloner } from "../services/program-cloner.js";
|
|
10
9
|
import { processRegistry } from "../services/process-registry.js";
|
|
11
10
|
import { portManager } from "../services/port-manager.js";
|
|
12
11
|
|
|
13
|
-
import type { Config, TokenConfig
|
|
12
|
+
import type { Config, TokenConfig } from "../types/config.js";
|
|
14
13
|
import type { ClonedToken } from "../services/token-cloner.js";
|
|
15
14
|
import type { RunningValidator } from "../services/process-registry.js";
|
|
16
15
|
|
|
@@ -11,8 +11,12 @@ import { runCommand } from "../utils/shell.js";
|
|
|
11
11
|
import { TokenCloner } from "./token-cloner.js";
|
|
12
12
|
import { ProgramCloner } from "./program-cloner.js";
|
|
13
13
|
import { mintTokenToWallet as mintTokenToWalletShared } from "../commands/mint.js";
|
|
14
|
+
import {
|
|
15
|
+
loadClonedTokens,
|
|
16
|
+
findTokenByMint,
|
|
17
|
+
type ClonedToken,
|
|
18
|
+
} from "../utils/token-loader.js";
|
|
14
19
|
import type { Config } from "../types/config.js";
|
|
15
|
-
import type { ClonedToken } from "./token-cloner.js";
|
|
16
20
|
|
|
17
21
|
export interface APIServerConfig {
|
|
18
22
|
port: number;
|
|
@@ -256,57 +260,10 @@ export class APIServer {
|
|
|
256
260
|
}
|
|
257
261
|
|
|
258
262
|
private async getClonedTokens(): Promise<ClonedToken[]> {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
this.config.workDir,
|
|
264
|
-
`token-${tokenConfig.symbol.toLowerCase()}`
|
|
265
|
-
);
|
|
266
|
-
const modifiedAccountPath = join(tokenDir, "modified.json");
|
|
267
|
-
const sharedMintAuthorityPath = join(
|
|
268
|
-
this.config.workDir,
|
|
269
|
-
"shared-mint-authority.json"
|
|
270
|
-
);
|
|
271
|
-
|
|
272
|
-
if (
|
|
273
|
-
existsSync(modifiedAccountPath) &&
|
|
274
|
-
existsSync(sharedMintAuthorityPath)
|
|
275
|
-
) {
|
|
276
|
-
try {
|
|
277
|
-
const mintAuthorityData = JSON.parse(
|
|
278
|
-
readFileSync(sharedMintAuthorityPath, "utf8")
|
|
279
|
-
);
|
|
280
|
-
let mintAuthority;
|
|
281
|
-
|
|
282
|
-
if (Array.isArray(mintAuthorityData)) {
|
|
283
|
-
const keypair = Keypair.fromSecretKey(
|
|
284
|
-
new Uint8Array(mintAuthorityData)
|
|
285
|
-
);
|
|
286
|
-
mintAuthority = {
|
|
287
|
-
publicKey: keypair.publicKey.toBase58(),
|
|
288
|
-
secretKey: Array.from(keypair.secretKey),
|
|
289
|
-
};
|
|
290
|
-
} else {
|
|
291
|
-
mintAuthority = mintAuthorityData;
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
clonedTokens.push({
|
|
295
|
-
config: tokenConfig,
|
|
296
|
-
mintAuthorityPath: sharedMintAuthorityPath,
|
|
297
|
-
modifiedAccountPath,
|
|
298
|
-
mintAuthority,
|
|
299
|
-
});
|
|
300
|
-
} catch (error) {
|
|
301
|
-
console.error(
|
|
302
|
-
`Failed to load cloned token ${tokenConfig.symbol}:`,
|
|
303
|
-
error
|
|
304
|
-
);
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
return clonedTokens;
|
|
263
|
+
return await loadClonedTokens(
|
|
264
|
+
this.config.config.tokens,
|
|
265
|
+
this.config.workDir
|
|
266
|
+
);
|
|
310
267
|
}
|
|
311
268
|
|
|
312
269
|
private async getClonedPrograms(): Promise<
|
|
@@ -341,14 +298,10 @@ export class APIServer {
|
|
|
341
298
|
amount: number
|
|
342
299
|
): Promise<any> {
|
|
343
300
|
const clonedTokens = await this.getClonedTokens();
|
|
344
|
-
const token = clonedTokens
|
|
345
|
-
(t) => t.config.mainnetMint === mintAddress
|
|
346
|
-
);
|
|
301
|
+
const token = findTokenByMint(clonedTokens, mintAddress);
|
|
347
302
|
|
|
348
303
|
if (!token) {
|
|
349
|
-
throw new Error(
|
|
350
|
-
`Token with mint address ${mintAddress} not found in cloned tokens`
|
|
351
|
-
);
|
|
304
|
+
throw new Error(`Token ${mintAddress} not found in cloned tokens`);
|
|
352
305
|
}
|
|
353
306
|
|
|
354
307
|
// Use the shared minting function from the mint command
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "fs";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import { Keypair } from "@solana/web3.js";
|
|
4
|
+
import type { TokenConfig } from "../types/config.js";
|
|
5
|
+
|
|
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
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Shared utility to load cloned tokens from the work directory.
|
|
19
|
+
* This ensures consistent token loading across CLI and API server.
|
|
20
|
+
*/
|
|
21
|
+
export async function loadClonedTokens(
|
|
22
|
+
tokenConfigs: TokenConfig[],
|
|
23
|
+
workDir: string = ".solforge"
|
|
24
|
+
): Promise<ClonedToken[]> {
|
|
25
|
+
const clonedTokens: ClonedToken[] = [];
|
|
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;
|
|
31
|
+
|
|
32
|
+
if (existsSync(sharedMintAuthorityPath)) {
|
|
33
|
+
try {
|
|
34
|
+
const fileContent = JSON.parse(
|
|
35
|
+
readFileSync(sharedMintAuthorityPath, "utf8")
|
|
36
|
+
);
|
|
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
|
+
};
|
|
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
|
+
}
|
|
63
|
+
|
|
64
|
+
if (!sharedMintAuthority) {
|
|
65
|
+
return [];
|
|
66
|
+
}
|
|
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");
|
|
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
|
+
};
|
|
82
|
+
|
|
83
|
+
// Add metadata path if it exists
|
|
84
|
+
if (existsSync(metadataAccountPath)) {
|
|
85
|
+
clonedToken.metadataAccountPath = metadataAccountPath;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
clonedTokens.push(clonedToken);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return clonedTokens;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Find a cloned token by its mint address
|
|
97
|
+
*/
|
|
98
|
+
export function findTokenByMint(
|
|
99
|
+
clonedTokens: ClonedToken[],
|
|
100
|
+
mintAddress: string
|
|
101
|
+
): ClonedToken | undefined {
|
|
102
|
+
return clonedTokens.find((token) => token.config.mainnetMint === mintAddress);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Find a cloned token by its symbol
|
|
107
|
+
*/
|
|
108
|
+
export function findTokenBySymbol(
|
|
109
|
+
clonedTokens: ClonedToken[],
|
|
110
|
+
symbol: string
|
|
111
|
+
): ClonedToken | undefined {
|
|
112
|
+
return clonedTokens.find(
|
|
113
|
+
(token) => token.config.symbol.toLowerCase() === symbol.toLowerCase()
|
|
114
|
+
);
|
|
115
|
+
}
|