Skip to content

Comments

add Genesis launch commands (create & register)#63

Open
MarkSackerberg wants to merge 8 commits intomainfrom
genesis-launch-commands
Open

add Genesis launch commands (create & register)#63
MarkSackerberg wants to merge 8 commits intomainfrom
genesis-launch-commands

Conversation

@MarkSackerberg
Copy link
Contributor

Add genesis launch create and genesis launch register CLI commands that use the Genesis SDK API module to create and register token launches via the Metaplex platform.

  • genesis launch create: all-in-one flow that calls the Genesis API, signs/sends transactions, and registers the launch
  • genesis launch register: registers an existing on-chain genesis account with the platform using a JSON config file
  • Both commands show API response details on validation errors
  • Bump @metaplex-foundation/genesis to ^0.20.3

Note: launch commands require the Genesis SDK API module (createLaunch, registerLaunch, createAndRegisterLaunch) which will be available once the SDK PR is merged and published.

Add `genesis launch create` and `genesis launch register` CLI commands
that use the Genesis SDK API module to create and register token launches
via the Metaplex platform.

- `genesis launch create`: all-in-one flow that calls the Genesis API,
  signs/sends transactions, and registers the launch
- `genesis launch register`: registers an existing on-chain genesis
  account with the platform using a JSON config file
- Both commands show API response details on validation errors
- Bump @metaplex-foundation/genesis to ^0.20.3

Note: launch commands require the Genesis SDK API module (createLaunch,
registerLaunch, createAndRegisterLaunch) which will be available once
the SDK PR is merged and published.
Copy link
Contributor

@blockiosaurus blockiosaurus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once the new SDK version is deployed let's rerun the tests, but otherwise looks good.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 24, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds Genesis launch CLI commands (create/register), updates add-launch-pool to apply end behaviors in a second transaction, renames transition invocation to triggerBehaviorsV2, introduces detectSvmNetwork utility, bumps @metaplex-foundation/genesis, and expands tests and test helpers.

Changes

Cohort / File(s) Summary
Dependency Update
package.json
Bumped @metaplex-foundation/genesis from ^0.18.0 to ^0.20.5.
New CLI Launch Commands
src/commands/genesis/launch/create.ts, src/commands/genesis/launch/register.ts, src/commands/genesis/launch/index.ts
Adds genesis launch subcommands: create (builds CreateLaunchInput, validates locked allocations JSON, calls createAndRegisterLaunch) and register (reads launchConfig JSON, calls registerLaunch).
Modified Genesis Commands
src/commands/genesis/bucket/add-launch-pool.ts, src/commands/genesis/transition.ts, src/commands/genesis/index.ts
add-launch-pool now omits endBehaviors in initial tx and applies them with a separate setLaunchPoolBucketV2Behaviors call; imports createClaimSchedule. transitionV2 call replaced with triggerBehaviorsV2. Minor help text added.
Utility
src/lib/util.ts
Adds detectSvmNetwork(RpcChain) mapping internal RpcChain to 'solana-mainnet' or 'solana-devnet'.
Tests & Helpers
test/commands/genesis/genesis.launch.test.ts, test/commands/genesis/genesishelpers.ts, test/runCli.ts
Adds comprehensive genesis launch tests; tweaks default claimStart by +1s; simplifies runCli to reject only on non-zero exit codes (removes stderr-based error tracking).

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant CLI as Genesis Launch Create
    participant FS as File System
    participant TxBuilder as Tx Builder / Wallet
    participant API as Metaplex Genesis API

    User->>CLI: Run `genesis launch create` with flags
    CLI->>FS: Load locked allocations JSON (optional)
    CLI->>CLI: Parse & validate flags, build CreateLaunchInput
    CLI->>TxBuilder: Build & submit on-chain creation tx (mint, accounts)
    TxBuilder-->>CLI: Return tx signatures / confirmations
    CLI->>API: Call createAndRegisterLaunch(payload, signatures)
    API-->>CLI: Return launch ID, token ID, mint
    CLI->>User: Print results & explorer links
Loading
sequenceDiagram
    actor User
    participant CLI as Genesis Launch Register
    participant FS as File System
    participant API as Metaplex Genesis API

    User->>CLI: Run `genesis launch register` <genesisAccount> --launchConfig=path
    CLI->>FS: Read launchConfig JSON
    CLI->>CLI: Validate & normalize launchInput (set network/wallet)
    CLI->>API: Call registerLaunch(genesisAccount, launchInput)
    alt New registration
        API-->>CLI: Success with Launch ID
        CLI->>User: Display launch details
    else Already registered
        API-->>CLI: Already-registered response
        CLI->>User: Display "already registered"
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • blockiosaurus
  • tonyboylehub
  • nhanphan
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the main change: adding two new Genesis launch CLI commands (create and register).
Description check ✅ Passed The description is directly related to the changeset, providing a clear overview of the two new commands, their purposes, and the dependency bump.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch genesis-launch-commands

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/commands/genesis/bucket/add-launch-pool.ts (1)

267-291: ⚠️ Potential issue | 🟠 Major

Second transaction's signature is silently discarded; user only sees the first tx.

When endBehaviors are provided, the second transaction (setLaunchPoolBucketV2Behaviors) is sent but its result is not captured. The output (Line 310) only displays the signature from the first addLaunchPoolBucketV2 transaction. If the second tx fails after the first succeeds, the bucket is left without end behaviors and the user has no signature to debug with.

Also, bucketPda is computed twice—once inside the if block (Line 269) and again unconditionally (Line 288). Hoist it above the conditional and reuse.

Proposed fix
+      // Get the bucket PDA (needed for both end behaviors and display)
+      const [bucketPda] = findLaunchPoolBucketV2Pda(this.context.umi, {
+        genesisAccount: genesisAddress,
+        bucketIndex,
+      })
+
       // Set end behaviors in a separate transaction if provided
       if (endBehaviors.length > 0) {
-        const [bucketPda] = findLaunchPoolBucketV2Pda(this.context.umi, {
-          genesisAccount: genesisAddress,
-          bucketIndex,
-        })
-
         spinner.text = 'Setting end behaviors...'
         const setBehaviorsTx = setLaunchPoolBucketV2Behaviors(this.context.umi, {
           genesisAccount: genesisAddress,
           bucket: bucketPda,
           authority: this.context.signer,
           payer: this.context.payer,
           padding: new Array(3).fill(0),
           endBehaviors,
         })
 
-        await umiSendAndConfirmTransaction(this.context.umi, setBehaviorsTx)
+        const setBehaviorsResult = await umiSendAndConfirmTransaction(this.context.umi, setBehaviorsTx)
+        // Log second transaction signature
+        this.log(`End Behaviors Transaction: ${txSignatureToString(setBehaviorsResult.transaction.signature as Uint8Array)}`)
       }
 
-      // Get the bucket PDA
-      const [bucketPda] = findLaunchPoolBucketV2Pda(this.context.umi, {
-        genesisAccount: genesisAddress,
-        bucketIndex,
-      })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/genesis/bucket/add-launch-pool.ts` around lines 267 - 291, Hoist
the bucket PDA computation by calling
findLaunchPoolBucketV2Pda(this.context.umi, { genesisAccount: genesisAddress,
bucketIndex }) once before the conditional, reuse that [bucketPda] inside the if
block, and when sending the behaviors transaction capture its return value
(signature) from umiSendAndConfirmTransaction for
setLaunchPoolBucketV2Behaviors; then include or log that signature alongside the
addLaunchPoolBucketV2 transaction signature so both results are surfaced to the
user instead of silently discarding the second tx result.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/commands/genesis/launch/create.ts`:
- Around line 184-189: The code unsafely casts this.context.commitment when
calling createAndRegisterLaunch; instead validate that this.context.commitment
is one of the allowed values ('processed' | 'confirmed' | 'finalized') before
passing it, and fall back to 'confirmed' if not; implement this by reading
this.context.commitment, checking against a set/array of allowed strings (or a
small helper like normalizeCommitment), and pass the validated value into
createAndRegisterLaunch (reference: call site createAndRegisterLaunch and the
this.context.commitment variable).
- Line 9: Replace direct fs usage with the project's file utility: import and
call readJsonSync from src/lib/file.ts instead of using fs.readFileSync +
JSON.parse when loading the allocations JSON (replace the fs import). Also
tighten the runtime validation of the loaded lockedAllocations: do not rely on
Array.isArray(...) + an as LockedAllocation[] assertion—iterate the array and
validate each object's required fields and types (e.g., check presence and types
of fields defined on the LockedAllocation interface) and throw a clear error if
any entry is malformed before proceeding.

In `@src/commands/genesis/launch/index.ts`:
- Around line 1-26: The GenesisLaunch class currently extends Command and
implements a custom run() that prints manual help text; change the class to
extend BaseCommand (instead of Command) to match project guidelines for
non-transactional commands with global flags, then remove the manual
help/logging from the run() method so the command relies on OCLIF's built-in
help (--help) and subcommand listing; ensure the class signature and any imports
are updated to reference BaseCommand and leave run() either empty or call
this._help() if explicit invocation is needed.

In `@src/commands/genesis/launch/register.ts`:
- Around line 71-78: Replace the direct fs usage when reading the launch config:
instead of calling fs.existsSync(filePath) and fs.readFileSync/JSON.parse into
launchConfig (typed as CreateLaunchInput), import and use the file helpers from
src/lib/file.ts (e.g., fileExists/fileExistsSync and readJsonFile or readJson)
to check existence and load/parse the JSON; update the top of the file to import
those helpers, use flags.launchConfig as the path, and assign the returned
parsed object to launchConfig (CreateLaunchInput).

In `@test/commands/genesis/genesis.launch.test.ts`:
- Around line 450-460: Remove the redundant regex assertions that validate the
format of addresses since createGenesisAccount and addLaunchPoolBucket already
validate and throw on failure; specifically, delete the
expect(genesisAddress).to.match(/^[a-zA-Z0-9]+$/) assertion after
createGenesisAccount and the analogous expect(bucketAddress).to.match(...)
assertion in the addLaunchPoolBucket test (the block around createGenesisAccount
and the block around addLaunchPoolBucket referenced at lines ~532-543) so the
tests rely on the helper functions' own validation and error handling instead of
duplicative regex checks.
- Around line 35-333: Several tests use hardcoded 2025 ISO timestamps (e.g.,
depositStartTime and vestingStartTime in the tests like "fails when required
flags are missing (no --depositStartTime)", "parses locked allocations file and
reaches API call", and others) which are now in the past; change these to
generate future-relative ISO timestamps at runtime (use a small helper or inline
logic to compute Date.now() + offset and call toISOString()) and replace the
hardcoded strings in the cliInput arrays and the tmpFile locked allocation
objects so runCli and the Genesis-related tests (referenced via runCli and
tmpFile variables) always receive future dates.

---

Outside diff comments:
In `@src/commands/genesis/bucket/add-launch-pool.ts`:
- Around line 267-291: Hoist the bucket PDA computation by calling
findLaunchPoolBucketV2Pda(this.context.umi, { genesisAccount: genesisAddress,
bucketIndex }) once before the conditional, reuse that [bucketPda] inside the if
block, and when sending the behaviors transaction capture its return value
(signature) from umiSendAndConfirmTransaction for
setLaunchPoolBucketV2Behaviors; then include or log that signature alongside the
addLaunchPoolBucketV2 transaction signature so both results are surfaced to the
user instead of silently discarding the second tx result.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 964e836 and 646256b.

⛔ Files ignored due to path filters (2)
  • .amman/genesis.so is excluded by !**/*.so
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (10)
  • package.json
  • src/commands/genesis/bucket/add-launch-pool.ts
  • src/commands/genesis/index.ts
  • src/commands/genesis/launch/create.ts
  • src/commands/genesis/launch/index.ts
  • src/commands/genesis/launch/register.ts
  • src/commands/genesis/transition.ts
  • test/commands/genesis/genesis.launch.test.ts
  • test/commands/genesis/genesishelpers.ts
  • test/runCli.ts

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

♻️ Duplicate comments (3)
test/commands/genesis/genesis.launch.test.ts (1)

544-546: Redundant type assertion — helper already validates on failure.

addLaunchPoolBucket throws on failure, making expect(result.bucketAddress).to.be.a('string') redundant. Based on learnings, the test suite prefers to rely on helper functions' own validation rather than duplicating assertions.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/commands/genesis/genesis.launch.test.ts` around lines 544 - 546, Remove
the redundant assertion that duplicates the helper's validation: delete the line
expect(result.bucketAddress).to.be.a('string') in the test so the test relies on
the helper addLaunchPoolBucket to throw/validate failures; leave the rest of the
test intact and keep using the result returned from addLaunchPoolBucket for any
further non-type checks.
src/commands/genesis/launch/register.ts (1)

74-75: existsSync still imported directly from node:fs.

The past review explicitly flagged both existsSync and readFileSync/JSON.parse. The latter was resolved with readJsonSync, but the direct node:fs existsSync call remains. As per coding guidelines, file handling operations should use utilities from src/lib/file.ts.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/genesis/launch/register.ts` around lines 74 - 75, Replace the
direct use/import of node:fs's existsSync in register.ts with the project's file
utility from src/lib/file.ts: remove the existsSync import from 'node:fs',
import the library helper (e.g., fileExists) from src/lib/file.ts, and change
the check `if (!existsSync(filePath))` to use that helper (`if
(!fileExists(filePath))`) so file existence checks follow the project's file
handling utilities; keep the thrown error message (`Launch config file not
found: ${filePath}`) unchanged.
src/commands/genesis/launch/index.ts (1)

1-13: Same Command vs BaseCommand concern as prior review.

GenesisLaunch extends Command rather than BaseCommand, inconsistent with the guideline for non-transactional commands. The run() calling only this.parse(GenesisLaunch) also produces no output when invoked without a subcommand; users will receive a silent no-op.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/genesis/launch/index.ts` around lines 1 - 13, The class
GenesisLaunch currently extends Command and its run() only calls
this.parse(GenesisLaunch), causing silent no-op and inconsistency; change the
class to extend BaseCommand (update the import to pull BaseCommand) and update
run() so after parsing it displays help/usage when no subcommand is provided
(e.g., call the command help helper such as this._help() / showHelp() from
BaseCommand) so users see output instead of silence; keep static
description/examples as-is and ensure the symbol names to edit are
GenesisLaunch, run, and the import that currently brings in Command.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/commands/genesis/bucket/add-launch-pool.ts`:
- Around line 271-290: The code currently treats failure of
setLaunchPoolBucketV2Behaviors as a full failure even though the primary
transaction creating the bucket (sent via umiSendAndConfirmTransaction for
variable transaction/result) may have succeeded; wrap the call to
setLaunchPoolBucketV2Behaviors and its umiSendAndConfirmTransaction in its own
try/catch so the main flow still marks the bucket creation as successful (call
spinner.succeed after the first transaction), and only handle behavior-setting
failures locally: on error set behaviorsSignature to undefined, call
spinner.warn or spinner.fail with a clear message like "Bucket created but
failed to set end behaviors; run setLaunchPoolBucketV2Behaviors(...) manually"
and include the error details (use the caught error and txSignatureToString if a
partial signature exists), rather than overriding the overall success state of
the bucket creation.

In `@src/commands/genesis/launch/create.ts`:
- Around line 156-158: The check `instanceof Date` in the validation for
entry.vestingStartTime is dead because JSON.parse never yields Date instances;
update the validation inside the create flow to only assert that
entry.vestingStartTime is a non-empty string (e.g. change the conditional that
currently reads `!entry.vestingStartTime || (typeof entry.vestingStartTime !==
'string' && !(entry.vestingStartTime instanceof Date))`) to a clear string check
for entry.vestingStartTime and change the thrown message (`Locked allocation
[${i}]: "vestingStartTime" must be a date string or Date`) to reflect the real
requirement (e.g. `"vestingStartTime" must be a date string`), keeping
references to the same variable names (entry.vestingStartTime and i) so the
change is localized.

In `@src/commands/genesis/launch/register.ts`:
- Line 78: The code casts the JSON read by readJsonSync(filePath) to
CreateLaunchInput without runtime checks; add structural validation after
reading into launchConfig to verify required top-level fields (e.g., token,
launch, launchType) are present and of expected types before proceeding,
returning a clear error message if validation fails; update the function that
uses launchConfig (referenced here as launchConfig and CreateLaunchInput) to
perform these checks (similar to the lockedAllocations checks in create.ts) so
malformed configs are rejected early with a helpful error instead of being sent
to the API.

In `@test/commands/genesis/genesis.launch.test.ts`:
- Around line 230-255: The temp file cleanup (fs.unlinkSync(tmpFile)) in the
tests 'fails when locked allocations file is not a JSON array' and similar tests
can be skipped if an assertion in the catch block throws; wrap the runCli/expect
logic in a try/finally so fs.unlinkSync(tmpFile) always runs: move creation of
tmpFile and fs.writeFileSync(tmpFile, ...) before the try, call await
runCli(...) and the expect(...) checks inside the try/catch as now (or use
try/catch inside the try), but ensure the final finally block performs
fs.unlinkSync(tmpFile); apply the same try/finally pattern to the other tests
that create tmpFile and call runCli (tests named 'parses locked allocations
file...' and the third similar test) to guarantee cleanup even on failing
assertions.
- Around line 453-464: The test suite shares mutable genesisAddress declared at
top-level and set inside the 'creates a genesis account for claimSchedule test'
it-block, which causes cascading failures if that it fails; move the account
creation into a setup hook (e.g., beforeAll or beforeEach) that calls
createGenesisAccount and assigns genesisAddress, or add explicit guards/skips at
the start of dependent tests to check genesisAddress and fail/skip with a clear
message; update references in this file to rely on the setup hook
(genesisAddress) instead of depending on the first it to run successfully, and
ensure the same fix is applied to the other affected block around lines 522-533.

---

Duplicate comments:
In `@src/commands/genesis/launch/index.ts`:
- Around line 1-13: The class GenesisLaunch currently extends Command and its
run() only calls this.parse(GenesisLaunch), causing silent no-op and
inconsistency; change the class to extend BaseCommand (update the import to pull
BaseCommand) and update run() so after parsing it displays help/usage when no
subcommand is provided (e.g., call the command help helper such as this._help()
/ showHelp() from BaseCommand) so users see output instead of silence; keep
static description/examples as-is and ensure the symbol names to edit are
GenesisLaunch, run, and the import that currently brings in Command.

In `@src/commands/genesis/launch/register.ts`:
- Around line 74-75: Replace the direct use/import of node:fs's existsSync in
register.ts with the project's file utility from src/lib/file.ts: remove the
existsSync import from 'node:fs', import the library helper (e.g., fileExists)
from src/lib/file.ts, and change the check `if (!existsSync(filePath))` to use
that helper (`if (!fileExists(filePath))`) so file existence checks follow the
project's file handling utilities; keep the thrown error message (`Launch config
file not found: ${filePath}`) unchanged.

In `@test/commands/genesis/genesis.launch.test.ts`:
- Around line 544-546: Remove the redundant assertion that duplicates the
helper's validation: delete the line
expect(result.bucketAddress).to.be.a('string') in the test so the test relies on
the helper addLaunchPoolBucket to throw/validate failures; leave the rest of the
test intact and keep using the result returned from addLaunchPoolBucket for any
further non-type checks.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 646256b and 57f024d.

📒 Files selected for processing (5)
  • src/commands/genesis/bucket/add-launch-pool.ts
  • src/commands/genesis/launch/create.ts
  • src/commands/genesis/launch/index.ts
  • src/commands/genesis/launch/register.ts
  • test/commands/genesis/genesis.launch.test.ts

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

♻️ Duplicate comments (2)
src/commands/genesis/launch/register.ts (1)

8-8: 🧹 Nitpick | 🔵 Trivial

existsSync from node:fs bypasses project file utilities.

readJsonSync is correctly imported from src/lib/file.ts, but the existence check still uses node:fs directly. Consider either adding an existence check helper to src/lib/file.ts or letting readJsonSync throw and catching the ENOENT error to produce the user-friendly message. This keeps the file I/O surface consolidated.

As per coding guidelines: "src/**/*.ts: Use utilities in src/lib/file.ts for file handling operations".

♻️ Alternative: catch ENOENT from readJsonSync
-import { existsSync } from 'node:fs'
...
       const filePath = flags.launchConfig
-      if (!existsSync(filePath)) {
-        throw new Error(`Launch config file not found: ${filePath}`)
-      }
-
-      const launchConfig = readJsonSync(filePath) as CreateLaunchInput
+      let launchConfig: CreateLaunchInput
+      try {
+        launchConfig = readJsonSync(filePath) as CreateLaunchInput
+      } catch (err) {
+        if ((err as NodeJS.ErrnoException).code === 'ENOENT') {
+          throw new Error(`Launch config file not found: ${filePath}`)
+        }
+        throw err
+      }

Also applies to: 72-76

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/genesis/launch/register.ts` at line 8, The file uses
node:fs.existsSync in register.ts which bypasses our src/lib/file.ts utilities;
either add and use an exported existsSync (or fileExists) helper in
src/lib/file.ts and replace the direct existsSync import in register.ts, or
remove the existsSync usage and instead call readJsonSync inside a try/catch in
register.ts (catch ENOENT and emit the same user-friendly message); update both
the initial import at the top (remove node:fs existsSync) and the later usage
sites around the block referenced (lines ~72-76) to consistently use the new
helper or the ENOENT-catching pattern.
src/commands/genesis/launch/create.ts (1)

9-9: Same existsSync from node:fs pattern as register.ts.

Same guideline concern applies here. Won't repeat the full rationale — see the comment on register.ts.

Also applies to: 132-136

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/genesis/launch/create.ts` at line 9, The import/usage of
existsSync from 'node:fs' should be replaced to avoid TOCTOU and race
conditions; update code in create.ts (the module-level import and the checks
around lines referenced 132-136) to use a safe existence check via try/catch
with fs.accessSync or fs.statSync (or the async fs.promises.access/stat inside
an async flow) instead of existsSync, and remove the existsSync import; ensure
the surrounding logic that depends on the file/directory presence uses the
try/catch result to proceed or handle missing resources.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/commands/genesis/launch/create.ts`:
- Around line 120-128: Extract the duplicated RpcChain -> SvmNetwork mapping
into a shared helper (e.g., export function detectSvmNetwork(chain: RpcChain):
SvmNetwork) and use it from both locations instead of repeating the conditional;
replace the block in create.ts that assigns network (currently using
flags.network and this.context.chain) with a call to
detectSvmNetwork(this.context.chain) when flags.network is not provided, and do
the same replacement in register.ts (the identical mapping at the other
location) so both files import and call detectSvmNetwork.
- Around line 144-167: Add validation for the optional cliff object inside the
per-entry loop that currently validates parsed entries before assigning
lockedAllocations; if entry.cliff is present ensure it is an object, that
entry.cliff.duration exists with a numeric duration.value and a duration.unit
contained in the existing validTimeUnits set, and that entry.cliff.unlockAmount
is a numeric value (and non-negative if that applies to your domain). Update the
same loop that references parsed and assigns lockedAllocations (and types
LockedAllocation) to throw a clear Error like the other checks when any of these
cliff sub-fields are missing or malformed.

In `@test/commands/genesis/genesis.launch.test.ts`:
- Around line 14-18: The hardcoded 10s wait inside the before hook after
runCli(["toolbox","sol","airdrop",...]) is fragile; replace the fixed
setTimeout(wait) by actively polling for confirmation (e.g., query the account
balance or transaction status using the same CLI helper runCli or an SDK until
the airdropped amount appears, with a short interval and a sensible timeout) and
only proceed to runCli(['toolbox','sol','wrap','50']) once confirmed; if you
prefer a simpler interim fix, extract the 10000 value into a named constant
(e.g., AIRDROP_CONFIRMATION_TIMEOUT_MS) with a comment explaining why that
duration was chosen and keep the wait logic in the before hook.
- Around line 354-385: The test "fails when genesis account argument is missing"
currently only asserts the error message is non-empty; update the assertion in
the catch block to check for a specific substring to make it meaningful (e.g.
assert the thrown Error from runCli(cliInput) contains 'Missing 1 required arg'
or the argument name 'genesisAccount'); locate the test by its description in
genesis.launch.test.ts and replace expect((error as
Error).message).to.not.be.empty with a tighter assertion such as expect((error
as Error).message).to.include('genesisAccount') (or include 'Missing 1 required
arg') so the test verifies the exact missing-argument error from runCli.
- Around line 40-206: Multiple nearly-identical tests asserting missing required
flags can be collapsed into a table-driven loop: create an allFlags map (keys:
name, symbol, image, tokenAllocation, depositStartTime, raiseGoal,
raydiumLiquidityBps, fundsRecipient) mapping to their CLI pairs (use futureIso(7
* 86400) for depositStartTime), then iterate over Object.keys(allFlags) to
generate an it(...) per omitted flag that builds cliInput =
['genesis','launch','create'] and pushes every flag pair except the omitted one,
calls runCli(cliInput) and asserts the thrown error message contains 'Missing
required flag' and the omitted key; replace the eight manual tests with this
single loop to reduce repetition while preserving the same assertions.

---

Duplicate comments:
In `@src/commands/genesis/launch/create.ts`:
- Line 9: The import/usage of existsSync from 'node:fs' should be replaced to
avoid TOCTOU and race conditions; update code in create.ts (the module-level
import and the checks around lines referenced 132-136) to use a safe existence
check via try/catch with fs.accessSync or fs.statSync (or the async
fs.promises.access/stat inside an async flow) instead of existsSync, and remove
the existsSync import; ensure the surrounding logic that depends on the
file/directory presence uses the try/catch result to proceed or handle missing
resources.

In `@src/commands/genesis/launch/register.ts`:
- Line 8: The file uses node:fs.existsSync in register.ts which bypasses our
src/lib/file.ts utilities; either add and use an exported existsSync (or
fileExists) helper in src/lib/file.ts and replace the direct existsSync import
in register.ts, or remove the existsSync usage and instead call readJsonSync
inside a try/catch in register.ts (catch ENOENT and emit the same user-friendly
message); update both the initial import at the top (remove node:fs existsSync)
and the later usage sites around the block referenced (lines ~72-76) to
consistently use the new helper or the ENOENT-catching pattern.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 57f024d and b2078ed.

📒 Files selected for processing (4)
  • src/commands/genesis/bucket/add-launch-pool.ts
  • src/commands/genesis/launch/create.ts
  • src/commands/genesis/launch/register.ts
  • test/commands/genesis/genesis.launch.test.ts

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
test/commands/genesis/genesis.launch.test.ts (1)

238-244: ⚠️ Potential issue | 🟡 Minor

Weak assertion: to.not.be.empty passes for any non-empty error.

This was flagged in a prior review. The assertion at line 241 would pass for any error message, not just the expected "missing argument" error. Tighten it to assert on a specific substring.

🔧 Suggested fix
             } catch (error) {
-                expect((error as Error).message).to.not.be.empty
+                const msg = (error as Error).message
+                expect(msg).to.satisfy(
+                    (m: string) => m.includes('genesisAccount') || m.includes('Missing'),
+                    'Expected error about missing genesisAccount argument',
+                )
             } finally {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/commands/genesis/genesis.launch.test.ts` around lines 238 - 244, The
current test's catch block uses a weak assertion expect((error as
Error).message).to.not.be.empty; tighten it to assert the specific expected
error message (e.g., check that runCli throws a "missing argument" error) by
replacing the non-empty check with a substring/assertion against the expected
text (use runCli and the caught error's message), keeping the cleanup of
tmpConfig with fs.unlinkSync in finally.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/commands/genesis/launch/create.ts`:
- Around line 70-84: Add client-side range validation for the
raydiumLiquidityBps flag inside the command's run() method: read the parsed
value for raydiumLiquidityBps (from the Flags parsing in create.ts), verify it's
an integer between 2000 and 10000 inclusive, and if out of range throw a
user-friendly error or exit with a clear message (e.g., "raydiumLiquidityBps
must be between 2000 and 10000"); do this check before calling any API or
constructing the payload so invalid inputs are rejected early.

---

Duplicate comments:
In `@test/commands/genesis/genesis.launch.test.ts`:
- Around line 238-244: The current test's catch block uses a weak assertion
expect((error as Error).message).to.not.be.empty; tighten it to assert the
specific expected error message (e.g., check that runCli throws a "missing
argument" error) by replacing the non-empty check with a substring/assertion
against the expected text (use runCli and the caught error's message), keeping
the cleanup of tmpConfig with fs.unlinkSync in finally.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b2078ed and b010248.

📒 Files selected for processing (4)
  • src/commands/genesis/launch/create.ts
  • src/commands/genesis/launch/register.ts
  • src/lib/util.ts
  • test/commands/genesis/genesis.launch.test.ts

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/commands/genesis/launch/create.ts`:
- Around line 9-136: Remove the direct existsSync check and rely on readJsonSync
for file handling: in the run method where flags.lockedAllocations and filePath
are used, delete the existsSync branch and instead call readJsonSync(filePath)
inside a try/catch; if readJsonSync throws an ENOENT (or file-not-found) error,
rethrow a friendly Error(`Locked allocations file not found: ${filePath}`) so
the behavior remains the same while using the src/lib/file.ts utilities (refer
to flags.lockedAllocations, filePath, and readJsonSync).

In `@test/commands/genesis/genesis.launch.test.ts`:
- Around line 90-133: Tests "fails when locked allocations file is not a JSON
array" and "parses locked allocations file and reaches API call" create tmpFile
using a hard-coded '/tmp' path; replace uses of '/tmp' (where tmpFile is
constructed and used with fs.writeFileSync/fs.unlinkSync) with
path.join(os.tmpdir(), <unique-name>) where <unique-name> includes a
test-specific identifier and a unique suffix (timestamp/UUID/random) so files
are portable on Windows and won't collide in parallel runs, and ensure existing
cleanup (fs.unlinkSync) still removes the generated file.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b010248 and a9b6620.

📒 Files selected for processing (2)
  • src/commands/genesis/launch/create.ts
  • test/commands/genesis/genesis.launch.test.ts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants