solforge 0.1.5 → 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 +55 -12
- package/package.json +1 -1
- package/src/commands/init.ts +2 -2
- package/src/commands/mint.ts +9 -57
- package/src/commands/start.ts +2 -3
- package/src/services/api-server.ts +24 -60
- package/src/utils/token-loader.ts +115 -0
package/README.md
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# SolForge
|
|
2
2
|
|
|
3
|
+
[](https://badge.fury.io/js/solforge)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
3
6
|
**SolForge** is a powerful Solana localnet orchestration tool that simplifies the process of setting up and managing local Solana development environments. It allows you to clone mainnet programs and tokens to your local validator, making it easy to develop and test Solana applications with real-world data.
|
|
4
7
|
|
|
5
8
|
## ✨ Features
|
|
@@ -15,15 +18,27 @@
|
|
|
15
18
|
- 🎨 **Beautiful CLI** - Colorful, intuitive command-line interface
|
|
16
19
|
- 🌐 **REST API** - Background API server for programmatic access to validator operations
|
|
17
20
|
|
|
18
|
-
##
|
|
21
|
+
## 📦 Installation
|
|
19
22
|
|
|
20
23
|
### Prerequisites
|
|
21
24
|
|
|
22
|
-
- [Bun](https://bun.sh) runtime
|
|
23
25
|
- [Solana CLI tools](https://docs.solana.com/cli/install-solana-cli-tools) installed and configured
|
|
24
|
-
- Node.js 18+ (
|
|
26
|
+
- Node.js 18+ or [Bun](https://bun.sh) runtime
|
|
25
27
|
|
|
26
|
-
###
|
|
28
|
+
### Install from npm (Recommended)
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# Install globally with npm
|
|
32
|
+
npm install -g solforge
|
|
33
|
+
|
|
34
|
+
# Or with bun
|
|
35
|
+
bun install -g solforge
|
|
36
|
+
|
|
37
|
+
# Or with yarn
|
|
38
|
+
yarn global add solforge
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Install from Source
|
|
27
42
|
|
|
28
43
|
```bash
|
|
29
44
|
# Clone the repository
|
|
@@ -33,14 +48,20 @@ cd solforge
|
|
|
33
48
|
# Install dependencies
|
|
34
49
|
bun install
|
|
35
50
|
|
|
36
|
-
# Build
|
|
37
|
-
bun run build
|
|
51
|
+
# Build and install globally
|
|
52
|
+
bun run build:npm
|
|
53
|
+
npm install -g .
|
|
54
|
+
```
|
|
38
55
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
56
|
+
### Verify Installation
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
solforge --version
|
|
60
|
+
solforge --help
|
|
42
61
|
```
|
|
43
62
|
|
|
63
|
+
## 🚀 Quick Start
|
|
64
|
+
|
|
44
65
|
### Basic Usage
|
|
45
66
|
|
|
46
67
|
1. **Initialize a new project**:
|
|
@@ -78,8 +99,8 @@ solforge start
|
|
|
78
99
|
# API available at http://127.0.0.1:3000/api
|
|
79
100
|
curl http://127.0.0.1:3000/api/health
|
|
80
101
|
|
|
81
|
-
# Mint tokens via API
|
|
82
|
-
curl -X POST http://127.0.0.1:3000/api/tokens/
|
|
102
|
+
# Mint tokens via API (using mint address)
|
|
103
|
+
curl -X POST http://127.0.0.1:3000/api/tokens/EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v/mint \
|
|
83
104
|
-H "Content-Type: application/json" \
|
|
84
105
|
-d '{"walletAddress": "YOUR_WALLET_ADDRESS", "amount": 1000}'
|
|
85
106
|
|
|
@@ -121,11 +142,14 @@ Start a localnet validator with the current configuration.
|
|
|
121
142
|
```bash
|
|
122
143
|
solforge start # Start with default settings
|
|
123
144
|
solforge start --debug # Start with debug logging
|
|
145
|
+
solforge start --network # Make API server accessible over network
|
|
146
|
+
solforge start --debug --network # Debug mode + network access
|
|
124
147
|
```
|
|
125
148
|
|
|
126
149
|
**Options:**
|
|
127
150
|
|
|
128
151
|
- `--debug` - Enable debug logging to see detailed command output
|
|
152
|
+
- `--network` - Make API server accessible over network (binds to 0.0.0.0 instead of 127.0.0.1)
|
|
129
153
|
|
|
130
154
|
#### `solforge list`
|
|
131
155
|
|
|
@@ -228,6 +252,25 @@ When run without arguments, `solforge mint` will:
|
|
|
228
252
|
- Prompt for amount to mint
|
|
229
253
|
- Handle SPL token account creation automatically
|
|
230
254
|
|
|
255
|
+
#### `solforge api-server [options]`
|
|
256
|
+
|
|
257
|
+
Start the API server as a standalone service (without validator).
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
solforge api-server # Start on default port 3000
|
|
261
|
+
solforge api-server --port 8080 # Custom port
|
|
262
|
+
solforge api-server --host 0.0.0.0 # Network accessible
|
|
263
|
+
solforge api-server --rpc-url http://localhost:8899 # Custom RPC
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
**Options:**
|
|
267
|
+
|
|
268
|
+
- `-p, --port <port>` - Port for API server (default: 3000)
|
|
269
|
+
- `--host <host>` - Host to bind to (default: 127.0.0.1, use 0.0.0.0 for network access)
|
|
270
|
+
- `--rpc-url <url>` - Validator RPC URL (default: http://127.0.0.1:8899)
|
|
271
|
+
- `--faucet-url <url>` - Validator faucet URL (default: http://127.0.0.1:9900)
|
|
272
|
+
- `--work-dir <dir>` - Work directory (default: ./.solforge)
|
|
273
|
+
|
|
231
274
|
#### `solforge reset`
|
|
232
275
|
|
|
233
276
|
Reset localnet ledger (coming soon).
|
|
@@ -275,7 +318,7 @@ Here's the complete schema:
|
|
|
275
318
|
"faucetAccounts": ["YourWalletPublicKeyHere"],
|
|
276
319
|
"port": 8899,
|
|
277
320
|
"faucetPort": 9900,
|
|
278
|
-
"reset":
|
|
321
|
+
"reset": false,
|
|
279
322
|
"logLevel": "info",
|
|
280
323
|
"bindAddress": "127.0.0.1",
|
|
281
324
|
"limitLedgerSize": 100000,
|
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
|
|
|
@@ -593,7 +592,7 @@ export async function startCommand(
|
|
|
593
592
|
);
|
|
594
593
|
console.log(
|
|
595
594
|
chalk.gray(
|
|
596
|
-
` - POST http://${endpointHost}:${apiServerPort}/api/tokens/{
|
|
595
|
+
` - POST http://${endpointHost}:${apiServerPort}/api/tokens/{mintAddress}/mint - Mint tokens`
|
|
597
596
|
)
|
|
598
597
|
);
|
|
599
598
|
console.log(
|
|
@@ -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;
|
|
@@ -122,9 +126,9 @@ export class APIServer {
|
|
|
122
126
|
});
|
|
123
127
|
|
|
124
128
|
// Mint tokens to a wallet
|
|
125
|
-
router.post("/tokens/:
|
|
129
|
+
router.post("/tokens/:mintAddress/mint", async (req, res) => {
|
|
126
130
|
try {
|
|
127
|
-
const {
|
|
131
|
+
const { mintAddress } = req.params;
|
|
128
132
|
const { walletAddress, amount } = req.body;
|
|
129
133
|
|
|
130
134
|
if (!walletAddress || !amount) {
|
|
@@ -133,6 +137,15 @@ export class APIServer {
|
|
|
133
137
|
});
|
|
134
138
|
}
|
|
135
139
|
|
|
140
|
+
// Validate mint address
|
|
141
|
+
try {
|
|
142
|
+
new PublicKey(mintAddress);
|
|
143
|
+
} catch {
|
|
144
|
+
return res.status(400).json({
|
|
145
|
+
error: "Invalid mint address",
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
136
149
|
// Validate wallet address
|
|
137
150
|
try {
|
|
138
151
|
new PublicKey(walletAddress);
|
|
@@ -150,7 +163,7 @@ export class APIServer {
|
|
|
150
163
|
}
|
|
151
164
|
|
|
152
165
|
const result = await this.mintTokenToWallet(
|
|
153
|
-
|
|
166
|
+
mintAddress,
|
|
154
167
|
walletAddress,
|
|
155
168
|
amount
|
|
156
169
|
);
|
|
@@ -247,57 +260,10 @@ export class APIServer {
|
|
|
247
260
|
}
|
|
248
261
|
|
|
249
262
|
private async getClonedTokens(): Promise<ClonedToken[]> {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
this.config.workDir,
|
|
255
|
-
`token-${tokenConfig.symbol.toLowerCase()}`
|
|
256
|
-
);
|
|
257
|
-
const modifiedAccountPath = join(tokenDir, "modified.json");
|
|
258
|
-
const sharedMintAuthorityPath = join(
|
|
259
|
-
this.config.workDir,
|
|
260
|
-
"shared-mint-authority.json"
|
|
261
|
-
);
|
|
262
|
-
|
|
263
|
-
if (
|
|
264
|
-
existsSync(modifiedAccountPath) &&
|
|
265
|
-
existsSync(sharedMintAuthorityPath)
|
|
266
|
-
) {
|
|
267
|
-
try {
|
|
268
|
-
const mintAuthorityData = JSON.parse(
|
|
269
|
-
readFileSync(sharedMintAuthorityPath, "utf8")
|
|
270
|
-
);
|
|
271
|
-
let mintAuthority;
|
|
272
|
-
|
|
273
|
-
if (Array.isArray(mintAuthorityData)) {
|
|
274
|
-
const keypair = Keypair.fromSecretKey(
|
|
275
|
-
new Uint8Array(mintAuthorityData)
|
|
276
|
-
);
|
|
277
|
-
mintAuthority = {
|
|
278
|
-
publicKey: keypair.publicKey.toBase58(),
|
|
279
|
-
secretKey: Array.from(keypair.secretKey),
|
|
280
|
-
};
|
|
281
|
-
} else {
|
|
282
|
-
mintAuthority = mintAuthorityData;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
clonedTokens.push({
|
|
286
|
-
config: tokenConfig,
|
|
287
|
-
mintAuthorityPath: sharedMintAuthorityPath,
|
|
288
|
-
modifiedAccountPath,
|
|
289
|
-
mintAuthority,
|
|
290
|
-
});
|
|
291
|
-
} catch (error) {
|
|
292
|
-
console.error(
|
|
293
|
-
`Failed to load cloned token ${tokenConfig.symbol}:`,
|
|
294
|
-
error
|
|
295
|
-
);
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
return clonedTokens;
|
|
263
|
+
return await loadClonedTokens(
|
|
264
|
+
this.config.config.tokens,
|
|
265
|
+
this.config.workDir
|
|
266
|
+
);
|
|
301
267
|
}
|
|
302
268
|
|
|
303
269
|
private async getClonedPrograms(): Promise<
|
|
@@ -327,17 +293,15 @@ export class APIServer {
|
|
|
327
293
|
}
|
|
328
294
|
|
|
329
295
|
private async mintTokenToWallet(
|
|
330
|
-
|
|
296
|
+
mintAddress: string,
|
|
331
297
|
walletAddress: string,
|
|
332
298
|
amount: number
|
|
333
299
|
): Promise<any> {
|
|
334
300
|
const clonedTokens = await this.getClonedTokens();
|
|
335
|
-
const token = clonedTokens
|
|
336
|
-
(t) => t.config.symbol.toLowerCase() === symbol.toLowerCase()
|
|
337
|
-
);
|
|
301
|
+
const token = findTokenByMint(clonedTokens, mintAddress);
|
|
338
302
|
|
|
339
303
|
if (!token) {
|
|
340
|
-
throw new Error(`Token ${
|
|
304
|
+
throw new Error(`Token ${mintAddress} not found in cloned tokens`);
|
|
341
305
|
}
|
|
342
306
|
|
|
343
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
|
+
}
|