add Genesis launch commands (create & register)#63
add Genesis launch commands (create & register)#63MarkSackerberg wants to merge 8 commits intomainfrom
Conversation
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.
blockiosaurus
left a comment
There was a problem hiding this comment.
Once the new SDK version is deployed let's rerun the tests, but otherwise looks good.
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughAdds 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 Changes
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
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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 | 🟠 MajorSecond transaction's signature is silently discarded; user only sees the first tx.
When
endBehaviorsare provided, the second transaction (setLaunchPoolBucketV2Behaviors) is sent but its result is not captured. The output (Line 310) only displays the signature from the firstaddLaunchPoolBucketV2transaction. 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,
bucketPdais computed twice—once inside theifblock (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
⛔ Files ignored due to path filters (2)
.amman/genesis.sois excluded by!**/*.sopnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (10)
package.jsonsrc/commands/genesis/bucket/add-launch-pool.tssrc/commands/genesis/index.tssrc/commands/genesis/launch/create.tssrc/commands/genesis/launch/index.tssrc/commands/genesis/launch/register.tssrc/commands/genesis/transition.tstest/commands/genesis/genesis.launch.test.tstest/commands/genesis/genesishelpers.tstest/runCli.ts
There was a problem hiding this comment.
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.
addLaunchPoolBucketthrows on failure, makingexpect(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:existsSyncstill imported directly fromnode:fs.The past review explicitly flagged both
existsSyncandreadFileSync/JSON.parse. The latter was resolved withreadJsonSync, but the directnode:fsexistsSynccall remains. As per coding guidelines, file handling operations should use utilities fromsrc/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: SameCommandvsBaseCommandconcern as prior review.
GenesisLaunchextendsCommandrather thanBaseCommand, inconsistent with the guideline for non-transactional commands. Therun()calling onlythis.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
📒 Files selected for processing (5)
src/commands/genesis/bucket/add-launch-pool.tssrc/commands/genesis/launch/create.tssrc/commands/genesis/launch/index.tssrc/commands/genesis/launch/register.tstest/commands/genesis/genesis.launch.test.ts
There was a problem hiding this comment.
Actionable comments posted: 5
♻️ Duplicate comments (2)
src/commands/genesis/launch/register.ts (1)
8-8: 🧹 Nitpick | 🔵 Trivial
existsSyncfromnode:fsbypasses project file utilities.
readJsonSyncis correctly imported fromsrc/lib/file.ts, but the existence check still usesnode:fsdirectly. Consider either adding an existence check helper tosrc/lib/file.tsor lettingreadJsonSyncthrow and catching theENOENTerror to produce the user-friendly message. This keeps the file I/O surface consolidated.As per coding guidelines: "
src/**/*.ts: Use utilities insrc/lib/file.tsfor 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: SameexistsSyncfromnode:fspattern asregister.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
📒 Files selected for processing (4)
src/commands/genesis/bucket/add-launch-pool.tssrc/commands/genesis/launch/create.tssrc/commands/genesis/launch/register.tstest/commands/genesis/genesis.launch.test.ts
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
test/commands/genesis/genesis.launch.test.ts (1)
238-244:⚠️ Potential issue | 🟡 MinorWeak assertion:
to.not.be.emptypasses 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
📒 Files selected for processing (4)
src/commands/genesis/launch/create.tssrc/commands/genesis/launch/register.tssrc/lib/util.tstest/commands/genesis/genesis.launch.test.ts
There was a problem hiding this comment.
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
📒 Files selected for processing (2)
src/commands/genesis/launch/create.tstest/commands/genesis/genesis.launch.test.ts
Add
genesis launch createandgenesis launch registerCLI 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 launchgenesis launch register: registers an existing on-chain genesis account with the platform using a JSON config fileNote: launch commands require the Genesis SDK API module (createLaunch, registerLaunch, createAndRegisterLaunch) which will be available once the SDK PR is merged and published.