@wiimdy/openfunderse 1.1.2 → 1.1.4

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.
@@ -0,0 +1,17 @@
1
+ # OpenFunderse participant env scaffold
2
+ # Copy values from your PARTICIPANT_PK, ADDRESS
3
+
4
+ RELAYER_URL=https://openfunderse-relayer-liard.vercel.app/
5
+ BOT_ID=bot-participant-1
6
+ BOT_API_KEY=replace_me
7
+
8
+ CHAIN_ID=10143
9
+ CLAIM_ATTESTATION_VERIFIER_ADDRESS=0x76AB8571cED9F4A81b733969216F53C1e13BC835
10
+ # Temporary bootstrap key (public and unsafe). Replace via bot-init before real usage.
11
+ PARTICIPANT_PRIVATE_KEY=__TEMP_PRIVATE_KEY__
12
+ PARTICIPANT_ADDRESS=0x00
13
+
14
+ PARTICIPANT_REQUIRE_EXPLICIT_SUBMIT=true
15
+ PARTICIPANT_AUTO_SUBMIT=false
16
+ # PARTICIPANT_TRUSTED_RELAYER_HOSTS=openfunderse-relayer.example.com
17
+ # PARTICIPANT_ALLOW_HTTP_RELAYER=true
@@ -0,0 +1,31 @@
1
+ # OpenFunderse strategy env scaffold
2
+ # Copy values from your STRATEGY_PK, ADDRESS
3
+
4
+ RELAYER_URL=https://openfunderse-relayer-liard.vercel.app/
5
+ BOT_ID=bot-strategy-1
6
+ BOT_API_KEY=replace_me
7
+ CHAIN_ID=10143
8
+ RPC_URL=https://testnet-rpc.monad.xyz
9
+
10
+ # NadFun / protocol addresses
11
+ INTENT_BOOK_ADDRESS=0x76AB8571cED9F4A81b733969216F53C1e13BC835
12
+ NADFUN_EXECUTION_ADAPTER_ADDRESS=0x7949DBd0779Cd829D1B2491A33C8EcCc88669Aa4
13
+ ADAPTER_ADDRESS=0x7949DBd0779Cd829D1B2491A33C8EcCc88669Aa4
14
+ VAULT_ADDRESS=0x34FB76f115f4C4E461eB90eA9f92faEF8c4A5E0A
15
+ NADFUN_LENS_ADDRESS=0xB056d79CA5257589692699a46623F901a3BB76f1
16
+ NADFUN_BONDING_CURVE_ROUTER=0x865054F0F6A288adaAc30261731361EA7E908003
17
+ NADFUN_DEX_ROUTER=0x5D4a4f430cA3B1b2dB86B9cFE48a5316800F5fb2
18
+ NADFUN_WMON_ADDRESS=0xFb8bE43D65FBC1290D6178C6DbA6E58c6D18fA60
19
+
20
+ # Strategy signer (EOA)
21
+ # Temporary bootstrap key (public and unsafe). Replace via bot-init before real usage.
22
+ STRATEGY_PRIVATE_KEY=__TEMP_PRIVATE_KEY__
23
+ STRATEGY_ADDRESS=0x00
24
+
25
+ # STRATEGY_CREATE_MIN_SIGNER_BALANCE_WEI=10000000000000000
26
+
27
+ # Safety defaults
28
+ STRATEGY_REQUIRE_EXPLICIT_SUBMIT=true
29
+ STRATEGY_AUTO_SUBMIT=false
30
+ # STRATEGY_TRUSTED_RELAYER_HOSTS=openfunderse-relayer.example.com
31
+ # STRATEGY_ALLOW_HTTP_RELAYER=true
package/README.md CHANGED
@@ -15,8 +15,11 @@ npx @wiimdy/openfunderse@latest bot-init --skill-name strategy --yes
15
15
  # or
16
16
  npx @wiimdy/openfunderse@latest bot-init --skill-name participant --yes
17
17
 
18
- # 3) load env in current shell
19
- set -a; source .env; set +a
18
+ # 3) (optional) load env in current shell
19
+ # @wiimdy/openfunderse-agents auto-loads .env.strategy / .env.participant by command role.
20
+ set -a; source .env.strategy; set +a
21
+ # or
22
+ set -a; source .env.participant; set +a
20
23
  ```
21
24
 
22
25
  ## Where Files Are Stored
@@ -30,5 +33,7 @@ set -a; source .env; set +a
30
33
  - Use only:
31
34
  - `openfunderse-strategy`
32
35
  - `openfunderse-participant`
33
- - Default env scaffold path is `.env`.
34
- - If you run both bots in the same directory, use `--env-path` to split files (for example `.env.strategy`, `.env.participant`).
36
+ - Default env scaffold path is role-based:
37
+ - strategy: `.env.strategy`
38
+ - participant: `.env.participant`
39
+ - Use `--env-path` only when you want a custom filename/location.
@@ -7,10 +7,13 @@ import os from "node:os";
7
7
  import path from "node:path";
8
8
  import { createInterface } from "node:readline/promises";
9
9
  import { fileURLToPath } from "node:url";
10
+ import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
10
11
 
11
12
  const THIS_FILE = fileURLToPath(import.meta.url);
12
13
  const PACKAGE_ROOT = path.resolve(path.dirname(THIS_FILE), "..");
13
14
  const PACKS_ROOT = path.join(PACKAGE_ROOT, "packs");
15
+ const STRATEGY_ENV_TEMPLATE_PATH = path.join(PACKAGE_ROOT, ".env.strategy.example");
16
+ const PARTICIPANT_ENV_TEMPLATE_PATH = path.join(PACKAGE_ROOT, ".env.participant.example");
14
17
  const DEFAULT_RUNTIME_PACKAGE = "@wiimdy/openfunderse-agents";
15
18
  const SUPPORTED_ENV_PROFILES = new Set(["strategy", "participant", "all"]);
16
19
  const SUPPORTED_BOT_INIT_ROLES = new Set(["strategy", "participant"]);
@@ -314,12 +317,37 @@ function runtimeEnvExamplePath(runtimeDir, runtimePackage) {
314
317
  return path.join(runtimeDir, "node_modules", ...runtimePackage.split("/"), ".env.example");
315
318
  }
316
319
 
320
+ function defaultEnvFileNameForProfile(profile) {
321
+ if (profile === "strategy") {
322
+ return ".env.strategy";
323
+ }
324
+ if (profile === "participant") {
325
+ return ".env.participant";
326
+ }
327
+ return ".env";
328
+ }
329
+
330
+ function applyTemplateTokens(template) {
331
+ return template.replaceAll("__TEMP_PRIVATE_KEY__", TEMP_PRIVATE_KEY);
332
+ }
333
+
334
+ async function readProfileTemplate(profile) {
335
+ const templatePath =
336
+ profile === "strategy" ? STRATEGY_ENV_TEMPLATE_PATH : PARTICIPANT_ENV_TEMPLATE_PATH;
337
+ const fallback = profile === "strategy" ? STRATEGY_ENV_TEMPLATE : PARTICIPANT_ENV_TEMPLATE;
338
+ if (!existsSync(templatePath)) {
339
+ return fallback;
340
+ }
341
+ const template = await readFile(templatePath, "utf8");
342
+ return applyTemplateTokens(template);
343
+ }
344
+
317
345
  async function buildEnvScaffold(profile, runtimeDir, runtimePackage) {
318
346
  if (profile === "strategy") {
319
- return STRATEGY_ENV_TEMPLATE;
347
+ return readProfileTemplate("strategy");
320
348
  }
321
349
  if (profile === "participant") {
322
- return PARTICIPANT_ENV_TEMPLATE;
350
+ return readProfileTemplate("participant");
323
351
  }
324
352
 
325
353
  const runtimeTemplate = runtimeEnvExamplePath(runtimeDir, runtimePackage);
@@ -327,7 +355,9 @@ async function buildEnvScaffold(profile, runtimeDir, runtimePackage) {
327
355
  return readFile(runtimeTemplate, "utf8");
328
356
  }
329
357
 
330
- return `${STRATEGY_ENV_TEMPLATE}\n\n${PARTICIPANT_ENV_TEMPLATE}`;
358
+ const strategy = await readProfileTemplate("strategy");
359
+ const participant = await readProfileTemplate("participant");
360
+ return `${strategy}\n\n${participant}`;
331
361
  }
332
362
 
333
363
  async function writeEnvScaffold(options) {
@@ -340,7 +370,7 @@ async function writeEnvScaffold(options) {
340
370
  const profile = normalizeEnvProfile(rawProfile);
341
371
  const envTarget = options.envFile
342
372
  ? path.resolve(options.envFile)
343
- : path.join(runtimeDir, ".env");
373
+ : path.join(runtimeDir, defaultEnvFileNameForProfile(profile));
344
374
 
345
375
  const alreadyExists = existsSync(envTarget);
346
376
  if (alreadyExists && !options.force) {
@@ -367,7 +397,7 @@ async function writeEnvScaffold(options) {
367
397
  }
368
398
 
369
399
  function defaultEnvPathForRole(role) {
370
- return path.join(process.cwd(), ".env");
400
+ return path.join(process.cwd(), `.env.${role}`);
371
401
  }
372
402
 
373
403
  function readAssignedEnvValue(content, key) {
@@ -418,43 +448,6 @@ function shellQuote(input) {
418
448
  return `'${input.replace(/'/g, `'\\''`)}'`;
419
449
  }
420
450
 
421
- async function runCommandCapture(cmd, args, cwd = process.cwd()) {
422
- return await new Promise((resolve, reject) => {
423
- const child = spawn(cmd, args, {
424
- cwd,
425
- stdio: ["ignore", "pipe", "pipe"]
426
- });
427
-
428
- const stdout = [];
429
- const stderr = [];
430
-
431
- child.stdout.on("data", (chunk) => {
432
- stdout.push(chunk);
433
- });
434
- child.stderr.on("data", (chunk) => {
435
- stderr.push(chunk);
436
- });
437
- child.on("error", (error) => {
438
- reject(error);
439
- });
440
- child.on("exit", (code) => {
441
- if (code === 0) {
442
- resolve({
443
- stdout: Buffer.concat(stdout).toString("utf8"),
444
- stderr: Buffer.concat(stderr).toString("utf8")
445
- });
446
- return;
447
- }
448
- const stderrText = Buffer.concat(stderr).toString("utf8").trim();
449
- reject(
450
- new Error(
451
- `command failed: ${cmd} ${args.join(" ")} (exit ${code})${stderrText ? ` - ${stderrText}` : ""}`
452
- )
453
- );
454
- });
455
- });
456
- }
457
-
458
451
  async function confirmBotInit({ role, envFile, privateKeyKey, isRotation, assumeYes }) {
459
452
  const mode = isRotation ? "wallet rotation" : "new wallet bootstrap";
460
453
  console.log("WARNING: bot-init will generate a new wallet and update your env private key.");
@@ -486,39 +479,11 @@ async function confirmBotInit({ role, envFile, privateKeyKey, isRotation, assume
486
479
  }
487
480
  }
488
481
 
489
- async function generateMonadWalletWithCast() {
490
- let output;
491
- try {
492
- output = await runCommandCapture("cast", ["wallet", "new", "--json"]);
493
- } catch (error) {
494
- const message = error instanceof Error ? error.message : String(error);
495
- if (message.includes("ENOENT")) {
496
- throw new Error(
497
- "cast is not installed. Install Foundry first (https://book.getfoundry.sh/getting-started/installation)."
498
- );
499
- }
500
- throw error;
501
- }
502
-
503
- let parsed;
504
- try {
505
- parsed = JSON.parse(output.stdout);
506
- } catch {
507
- throw new Error(`failed to parse cast output as JSON: ${output.stdout}`);
508
- }
509
-
510
- const first = Array.isArray(parsed) ? parsed[0] : parsed;
511
- const address = String(first?.address ?? "");
512
- const privateKey = String(first?.private_key ?? first?.privateKey ?? "");
513
- if (!/^0x[0-9a-fA-F]{40}$/.test(address)) {
514
- throw new Error(`invalid wallet address from cast: ${address}`);
515
- }
516
- if (!/^0x[0-9a-fA-F]{64}$/.test(privateKey)) {
517
- throw new Error("invalid private key from cast output");
518
- }
519
-
482
+ function generateMonadWalletWithViem() {
483
+ const privateKey = generatePrivateKey();
484
+ const account = privateKeyToAccount(privateKey);
520
485
  return {
521
- address,
486
+ address: account.address,
522
487
  privateKey
523
488
  };
524
489
  }
@@ -551,10 +516,13 @@ async function persistWallet(role, wallet, options) {
551
516
  if (rawName.includes("/") || rawName.includes("\\")) {
552
517
  throw new Error("--wallet-name must be a file name, not a path");
553
518
  }
554
- const fileName = rawName.endsWith(".json") ? rawName : `${rawName}.json`;
555
- const walletPath = path.join(walletDir, fileName);
556
- if (existsSync(walletPath) && !options.force) {
557
- throw new Error(`wallet file already exists: ${walletPath} (use --force to overwrite)`);
519
+ const baseName = rawName.endsWith(".json") ? rawName.slice(0, -".json".length) : rawName;
520
+ const walletPath = path.join(walletDir, `${baseName}.json`);
521
+ const privateKeyPath = path.join(walletDir, `${baseName}.private-key`);
522
+ if ((existsSync(walletPath) || existsSync(privateKeyPath)) && !options.force) {
523
+ throw new Error(
524
+ `wallet file already exists: ${walletPath} or ${privateKeyPath} (use --force to overwrite)`
525
+ );
558
526
  }
559
527
 
560
528
  const payload = {
@@ -565,11 +533,22 @@ async function persistWallet(role, wallet, options) {
565
533
  privateKey: wallet.privateKey
566
534
  };
567
535
  await mkdir(walletDir, { recursive: true, mode: 0o700 });
536
+ if (options.force) {
537
+ await rm(walletPath, { force: true });
538
+ await rm(privateKeyPath, { force: true });
539
+ }
568
540
  await writeFile(walletPath, `${JSON.stringify(payload, null, 2)}\n`, {
569
541
  mode: 0o600
570
542
  });
543
+ await writeFile(privateKeyPath, `${wallet.privateKey}\n`, {
544
+ mode: 0o600
545
+ });
571
546
  await chmod(walletPath, 0o600);
572
- return walletPath;
547
+ await chmod(privateKeyPath, 0o600);
548
+ return {
549
+ walletPath,
550
+ privateKeyPath
551
+ };
573
552
  }
574
553
 
575
554
  function assertPrivateKeyRotationAllowed(envContent, role, force) {
@@ -609,8 +588,8 @@ async function runBotInit(options) {
609
588
  assumeYes: options.yes
610
589
  });
611
590
 
612
- const wallet = await generateMonadWalletWithCast();
613
- const walletPath = await persistWallet(role, wallet, options);
591
+ const wallet = generateMonadWalletWithViem();
592
+ const walletFiles = await persistWallet(role, wallet, options);
614
593
  const updates = roleEnvUpdates(role, wallet);
615
594
  const nextEnvContent = upsertEnvValues(envContent, updates);
616
595
 
@@ -622,7 +601,8 @@ async function runBotInit(options) {
622
601
  console.log(`Initialized ${role} bot wallet for Monad testnet (${DEFAULT_MONAD_CHAIN_ID}).`);
623
602
  console.log(`Address: ${wallet.address}`);
624
603
  console.log(`Env file updated: ${envFile}`);
625
- console.log(`Wallet backup (keep secret): ${walletPath}`);
604
+ console.log(`Wallet backup (keep secret): ${walletFiles.walletPath}`);
605
+ console.log(`Private key backup (keep secret): ${walletFiles.privateKeyPath}`);
626
606
  console.log(`Load env now: ${sourceCommand}`);
627
607
  }
628
608
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wiimdy/openfunderse",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
4
4
  "description": "Install OpenFunderse skill packs into Codex",
5
5
  "type": "module",
6
6
  "bin": {
@@ -9,8 +9,13 @@
9
9
  "files": [
10
10
  "bin",
11
11
  "packs",
12
+ ".env.strategy.example",
13
+ ".env.participant.example",
12
14
  "README.md"
13
15
  ],
16
+ "dependencies": {
17
+ "viem": "^2.38.2"
18
+ },
14
19
  "engines": {
15
20
  "node": ">=20"
16
21
  },
@@ -48,7 +48,7 @@ npx @wiimdy/openfunderse@latest bot-init \
48
48
  4) Load env for the current shell:
49
49
 
50
50
  ```bash
51
- set -a; source .env; set +a
51
+ set -a; source .env.participant; set +a
52
52
  ```
53
53
 
54
54
  Note:
@@ -70,7 +70,7 @@ npx @wiimdy/openfunderse@latest bot-init \
70
70
  4) Load env for the current shell:
71
71
 
72
72
  ```bash
73
- set -a; source .env; set +a
73
+ set -a; source .env.strategy; set +a
74
74
  ```
75
75
 
76
76
  Note: