Skip to content

Feature/c4 migration#672

Open
mindrunner wants to merge 36 commits intomainfrom
feature/c4-migration
Open

Feature/c4 migration#672
mindrunner wants to merge 36 commits intomainfrom
feature/c4-migration

Conversation

@mindrunner
Copy link
Owner

No description provided.

@mindrunner mindrunner force-pushed the feature/c4-migration branch from 6ca4756 to 773f339 Compare January 21, 2026 14:13
- Update deps: @solana/kit, solana-kite, @staratlas/dev-sage
- Remove: @solana/web3.js, @coral-xyz/anchor, @staratlas/sage
- Rewrite: connection, wallet, tx sending with Kit patterns
- Delete: anchor.ts, programs.ts (no longer needed)
- Add: C4 migration plan in docs/
- Add account-registry.ts with discriminator-based decoding
- Add account-fetcher.ts with fetchAllAccounts/fetchAccount utils
- Add types.ts with AccountWithKey and C4 type re-exports
- Rewrite user-account.ts with Profile/Character/KeyPairSigner
- Rewrite game.ts to use account-fetcher
- Rewrite fleet-state types for __kind discriminators
- Update coordinates.ts to use bigint (no more BN)
- Update transform-sector/time for bigint
- Delete obsolete type-guard files (replaced by __kind)
Rewrote core fleet actions using dev-sage instruction functions:
- dock/undock: getIdleToDockedInstruction, getDockedToIdleInstruction
- move/end-move/stop-subwarp: getWarpToCoordinateInstruction, getStartSubwarpInstruction, getTheOneFleetStateHandlerInstruction, getStopSubwarpInstruction
- mine/end-mine: getStartMiningAsteroidInstruction, getStopMiningAsteroidInstruction
- cargo: getTransferCargoToFleetInstruction with cargoId-based transfers

Deleted ix/ folder (23 files) - inlined into act layer
Removed non-core act files (create-fleet, deposit-cargo, etc.)

Patterns: AccountWithKey<T>, KeyPairSigner, Address, fleet.state.__kind
Error reduction: 588 -> 392 (33%)
- Rewrite fleet-cargo.ts: cargo now on Fleet struct directly
- Rewrite user-fleets.ts: use fetchAllAccounts pattern
- Delete cargo-types.ts, cargo-stats-definition.ts, get-fleet-ships.ts: obsolete in C4
- Rewrite starbases.ts: starbase now embedded in StarSystem
- Rewrite starbase-by-*.ts: use StarSystem with starbase field
- Rewrite starbase-player.ts: use findStarbasePlayerPda pattern
- Rewrite planets*.ts: Planet -> CelestialBody
- Rewrite resources.ts, mine-items.ts: resource data embedded in CelestialBody
- Rewrite world-map.ts: new WorldMap structure for C4
- Stub profile.ts, pod-cleanup.ts, get-random-fleet.ts: TODO for C4
- Fix ships.ts: remove legacy imports
- Update util.ts, show-fleet-*.ts, settle-fleet.ts: use C4 types

Error count: 433 -> 317 (~27% reduction)
- Migrate all FSM files to C4 types (kind, Docked, coordinates)
- Update strategy files: Game → AccountWithKey<Game>
- Fix resource refs: game.data.mints → resource config
- Remove legacy R4 code: src/lib/, fleetbot/, airdrop/, service/fleet, service/gm
- Fix priority fee types: IInstruction → Instruction
- Clean compile with 0 errors
- Rewrite airdrop.ts using Kit types (Address, KeyPairSigner)
- Use superagent directly for airdrop requests
- Integrate with C4 player context for profile checking
Remove deleted fleetbot entry from tsup.config.ts, package.json
script, and docker/bin/fleetbot.sh to fix CI/CD build failures.
@mindrunner mindrunner force-pushed the feature/c4-migration branch from 7a8500c to a0d9bb9 Compare January 21, 2026 14:16
C4 stores coordinates as fixed-point with 56 fractional bits.
Use .toNumber() and Math.round() for display/matching.
Add .toRawTuple() for on-chain instructions.
- Add pre-flight validation for move/dock/undock (state, fuel, cooldown)
- Wrap transactions in try-catch, return success/failure instead of throwing
- Don't retry TransactionFailedError - on-chain failures won't succeed
- Fix fuel level reading from fleet.fuelTank instead of cargoHold
- Use character.playerProfile consistently for SAGE instructions
- Fix dynamic ship ID lookup in getRandomFleet
- implement tx simulation for compute unit estimation
- batch ship deposits to stay under 1232 byte tx limit
- handle C4 state struct changes (from/to, duration)
- reduce max fleet size 145→100 for safety margin
- skip sending txs that fail simulation with program error
…ort pairing

- Strategy now uses actual fleet locations to assign mining/transport pairs
- Added recovery logic for fleets stuck at incompatible (wrong faction) starbases
- Transports deliver to their paired miner's actual location
- Added findNearestCompatibleStarbase for recovery to faction-owned starbases
- Self-destruct fallback when no compatible starbase is reachable
- Fixed cargo availability checks before attempting refuel operations
- Improved logging for stuck fleets with location context
- Fixed lint/prettier issues with negated conditions
…System

The disband was failing with "System mismatch" because it passed
player.homeSystem but the fleet could be docked at a different nearby
starbase (within fuzzy match radius). Now uses the actual
fleetInfo.fleetState.data.system.
When starbase has insufficient crew for fleet creation, automatically
add crew using devAddCrew instruction (available on dev/test networks).
This allows the bot to create fleets without manually depositing crew.
- Recognize R4 mint addresses (fuel, ammo, food, tool) in resolveCargoId
- Skip move to exact coordinates if distance is negligible (< 0.01)
- Handle already-docked fleets gracefully in fsmDock
- Revert devAddCrew attempt (requires admin key)
Add preciseDistanceFrom method to Coordinates that uses full raw
coordinate precision instead of integer-rounded values. This fixes
the "Not within range" error when a fleet is close to (but not exactly
at) starbase coordinates - the integer distance appeared < 1 unit but
the precise distance was ~0.4 units.
Treat "Status mismatch - expected X actual Y" errors as success when
the fleet is already in the target state (Docked for dock, Idle for
undock). This handles race conditions where the fleet state changed
between fetching and transaction execution.
- Add game-resources.ts to derive R4 mints from game.cargoDefinitions
- Use known cargo IDs (fuel/ammo from fleet, food=4, tool=5)
- Remove dependency on FUEL_MINT, AMMO_MINT, FOOD_MINT, TOOL_MINT config
- Update strategies to use getR4MintSet(game) instead of config mints
- Simplify resource.ts (only keep atlas mint)

This fixes the "R4 cargo IDs empty" error when config mints don't match
the game's cargo definitions (e.g., atlasnet vs mainnet).
- Remove FUEL_MINT, AMMO_MINT, FOOD_MINT, TOOL_MINT, ATLAS_MINT from config
- Delete resource.ts (R4 mints now come from game data)
- R4 resources obtained via getR4MintSet() from game.cargoDefinitions
- Get R4 resources (fuel, ammo, food, tool) from game.resources
- Catch all SimulationProgramError gracefully (Status mismatch,
  Insufficient amount, UnbalancedInstruction)
- Catch TransactionFailedError for on-chain failures
- Remove verbose error/debug logging for expected failures
- Return false/failure instead of throwing exceptions
- Add clear warnings when fleets waiting for resources
C4 has no self-destruct instruction. Fleets stuck at incompatible
starbases will remain stuck until:
1. Starbase is conquered by player's faction
2. Fleet is destroyed in combat

Updated warnings to clearly explain the situation instead of
calling a non-existent function.
@mindrunner mindrunner requested a review from Copilot February 8, 2026 08:43
@mindrunner mindrunner marked this pull request as ready for review February 8, 2026 08:43
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Migrates the codebase toward Star Atlas “C4” by replacing legacy Solana/web3.js + Anchor + older StarAtlas SDK usage with @solana/kit + @staratlas/dev-* packages, while removing deprecated fleetbot/refill/telegram/Gm-market components.

Changes:

  • Replace wallet/keypair + instruction building with @solana/kit and @solana-program/* primitives.
  • Introduce discriminator-based account fetching/typing for dev-sage accounts and update fleet/state/strategy modules accordingly.
  • Remove legacy fleetbot entrypoint and a large set of R3-era market/refill/telegram modules; update build, Docker, and CI to match.

Reviewed changes

Copilot reviewed 197 out of 200 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
tsup.config.ts Removes fleetbot build entry from bundling targets
src/service/wallet/resource.ts Removes legacy mint constants tied to removed config fields
src/service/wallet/init-keypair.ts Migrates keypair loading/derivation to @solana/kit and introduces promise-based init
src/service/wallet/index.ts Stops exporting removed wallet resource module
src/service/sol/priority-fee/priority-fee-instruction.ts Replaces web3.js compute budget IX creation with @solana-program/compute-budget
src/service/sol/priority-fee/bloxroute-tip-instruction.ts Migrates SOL transfer instruction creation to @solana-program/system
src/service/sol/get-account.ts Migrates ATA derivation to @solana-program/token PDA helpers
src/service/sol/const/market-program.ts Moves program address parsing to @solana/kit address helper
src/service/sol/const/fleet-program.ts Moves program address parsing to @solana/kit address helper
src/service/sol/const/connection.ts Replaces web3.js Connection with solana-kite connect
src/service/sol/anchor.ts Removes Anchor provider (legacy SDK path)
src/service/gm/market.ts Removes legacy GM market/refill purchasing logic
src/service/gm/index.ts Removes legacy GM exports
src/service/fleet/refill-fleet.ts Removes legacy fleet refilling instruction builder
src/service/fleet/index.ts Removes legacy fleet service exports
src/service/fleet/const/index.ts Removes legacy fleet const re-exports
src/service/fleet/const/amounts.ts Removes legacy refill Amounts type
src/main/fleetbot/index.ts Removes legacy fleetbot entrypoint
src/main/fleetbot/fleetbot.ts Removes legacy fleetbot runtime/cron wiring
src/main/basedbot/lib/util/pod-cleanup.ts Stubs cargo pod cleanup for C4 pending confirmation/implementation
src/main/basedbot/lib/types.ts Adds common typed wrappers and re-exports for dev-sage/dev-profile packages
src/main/basedbot/lib/sage/util.ts Reworks name extraction utilities for C4 account shapes
src/main/basedbot/lib/sage/state/user-fleets.ts Migrates fleet querying/state aggregation to C4 fetcher + dev-sage types
src/main/basedbot/lib/sage/state/starbase-by-key.ts Migrates starbase lookup to StarSystem-based model
src/main/basedbot/lib/sage/state/starbase-by-coordinates.ts Adds coordinate-based StarSystem lookup with tolerance + precise coordinate helpers
src/main/basedbot/lib/sage/state/show-fleet-info.ts Updates fleet-state display logic for C4 state kinds and system/body lookups
src/main/basedbot/lib/sage/state/show-fleet-cargo-info.ts Updates cargo display for new C4 cargo model fields
src/main/basedbot/lib/sage/state/settle-fleet.ts Updates settlement logic for C4 actions and new handler calls
src/main/basedbot/lib/sage/state/planets.ts Renames/aliases planets → celestial bodies for C4
src/main/basedbot/lib/sage/state/planets-by-coordinates.ts Finds celestial bodies via StarSystem coordinate mapping
src/main/basedbot/lib/sage/state/planet-by-key.ts Migrates planet lookup to CelestialBody lookup
src/main/basedbot/lib/sage/state/mine-items.ts Replaces MineItem accounts with CelestialBody-based mining model
src/main/basedbot/lib/sage/state/get-fleet-ships.ts Removes legacy FleetShips lookup (R3 model)
src/main/basedbot/lib/sage/state/game.ts Adds Game singleton fetching via new account fetcher
src/main/basedbot/lib/sage/state/game-resources.ts Adds game-derived resource mint discovery (R4) with caching helpers
src/main/basedbot/lib/sage/state/cargo-types.ts Removes legacy cargo type resolution logic
src/main/basedbot/lib/sage/state/cargo-stats-definition.ts Removes legacy cargo stats definition fetcher
src/main/basedbot/lib/sage/ships.ts Shifts ship data sourcing to API-only stub for C4 with TODO for on-chain sizing
src/main/basedbot/lib/sage/ix/withdraw-cargo.ts Removes legacy IX builder
src/main/basedbot/lib/sage/ix/warp.ts Removes legacy IX builder
src/main/basedbot/lib/sage/ix/unload-cargo.ts Removes legacy IX builder
src/main/basedbot/lib/sage/ix/undock.ts Removes legacy IX builder
src/main/basedbot/lib/sage/ix/subwarp.ts Removes legacy IX builder
src/main/basedbot/lib/sage/ix/stop-warp.ts Removes legacy IX builder
src/main/basedbot/lib/sage/ix/stop-subwarp.ts Removes legacy IX builder
src/main/basedbot/lib/sage/ix/stop-mining.ts Removes legacy IX builder
src/main/basedbot/lib/sage/ix/start-mining.ts Removes legacy IX builder
src/main/basedbot/lib/sage/ix/movement-subwarp-handler.ts Removes legacy IX builder
src/main/basedbot/lib/sage/ix/load-cargo.ts Removes legacy IX builder
src/main/basedbot/lib/sage/ix/idle-to-respawn.ts Removes legacy IX builder
src/main/basedbot/lib/sage/ix/force-drop-fleet-cargo.ts Removes legacy IX builder
src/main/basedbot/lib/sage/ix/fleet-state-handler.ts Removes legacy mining handler IX builder
src/main/basedbot/lib/sage/ix/exit-respawn.ts Removes legacy IX builder
src/main/basedbot/lib/sage/ix/dock.ts Removes legacy IX builder
src/main/basedbot/lib/sage/ix/disbanded-fleet-to-escrow.ts Removes legacy IX builder
src/main/basedbot/lib/sage/ix/disband-fleet.ts Removes legacy IX builder
src/main/basedbot/lib/sage/ix/deposit-cargo.ts Removes legacy IX builder
src/main/basedbot/lib/sage/ix/create-fleet.ts Removes legacy IX builder
src/main/basedbot/lib/sage/ix/close-disbanded-fleet.ts Removes legacy IX builder
src/main/basedbot/lib/sage/ix/add-ship-to-fleet.ts Removes legacy IX builder
src/main/basedbot/lib/sage/ix/add-ship-escrow.ts Removes legacy IX builder
src/main/basedbot/lib/sage/act/withdraw-cargo.ts Removes legacy action implementation
src/main/basedbot/lib/sage/act/unload-all-cargo.ts Re-exports unloadAllCargo for compatibility
src/main/basedbot/lib/sage/act/undock.ts Implements undock using dev-sage instructions with validation and error handling
src/main/basedbot/lib/sage/act/stop-subwarp.ts Implements stop-subwarp via dev-sage instruction
src/main/basedbot/lib/sage/act/self-destruct.ts Stubs self-destruct for C4 (TODO)
src/main/basedbot/lib/sage/act/refuel.ts Implements refuel via generic loadCargo (C4 cargo IDs)
src/main/basedbot/lib/sage/act/rearm.ts Implements rearm via generic loadCargo (C4 cargo IDs)
src/main/basedbot/lib/sage/act/mine.ts Implements start-mining via dev-sage instruction and asteroid resource ID extraction
src/main/basedbot/lib/sage/act/exit-respawn.ts Removes legacy exit-respawn implementation
src/main/basedbot/lib/sage/act/end-move.ts Implements end-move via TheOneFleetStateHandler and adds arrival-time helpers
src/main/basedbot/lib/sage/act/disband-fleet.ts Implements disband via dev-sage instruction gated on Docked state
src/main/basedbot/lib/programs.ts Removes legacy Anchor Program construction / program ID map
src/main/basedbot/lib/fleet-state/types.ts Updates fleet state discriminators/data types for C4 state model
src/main/basedbot/lib/fleet-state/type-guard/starbase-loading-bay.ts Removes legacy raw type guards
src/main/basedbot/lib/fleet-state/type-guard/respawn.ts Removes legacy raw type guards
src/main/basedbot/lib/fleet-state/type-guard/move-warp.ts Removes legacy raw type guards
src/main/basedbot/lib/fleet-state/type-guard/move-sub-warp.ts Removes legacy raw type guards
src/main/basedbot/lib/fleet-state/type-guard/mine-asteroid.ts Removes legacy raw type guards
src/main/basedbot/lib/fleet-state/type-guard/idle.ts Removes legacy raw type guards
src/main/basedbot/lib/fleet-state/transform/transform-time.ts Updates timestamp transform to accept bigint/number
src/main/basedbot/lib/fleet-state/transform/transform-sector.ts Adds C4 fixed-point coordinate parsing with raw precision preservation
src/main/basedbot/lib/account-registry.ts Adds centralized registry for discriminator + decoder based account fetching
src/main/basedbot/lib/account-fetcher.ts Adds fetch helpers using solana-kite connection factories
src/main/basedbot/index.ts Adds Node workaround for isSecureContext expectation from bundled browser build
src/main/basedbot/fsm/info.ts Updates FSM info state handling for C4 state kinds/data fields
src/main/basedbot/fsm/configs/mine/mine.ts Updates mine config selection for C4 mineable shape + safety checks
src/main/basedbot/fsm/configs/mine/mine-titanium-ore.ts Adds null checks and uses typed imports
src/main/basedbot/fsm/configs/mine/mine-silicia.ts Adds null checks and uses typed imports
src/main/basedbot/fsm/configs/mine/mine-rochinol.ts Adds null checks and uses typed imports
src/main/basedbot/fsm/configs/mine/mine-nitrogen.ts Adds null checks and uses typed imports
src/main/basedbot/fsm/configs/mine/mine-lumanite.ts Adds null checks and uses typed imports
src/main/basedbot/fsm/configs/mine/mine-iron-ore.ts Adds null checks and uses typed imports
src/main/basedbot/fsm/configs/mine/mine-hydrogen.ts Adds null checks and uses typed imports
src/main/basedbot/fsm/configs/mine/mine-diamond.ts Adds null checks and uses typed imports
src/main/basedbot/fsm/configs/mine/mine-copper-ore.ts Adds null checks and uses typed imports
src/main/basedbot/fsm/configs/mine/mine-carbon.ts Adds null checks and uses typed imports
src/main/basedbot/fsm/configs/mine/mine-biomass.ts Adds null checks (but includes a coordinate mismatch bug; see suggestions)
src/main/basedbot/fleet-strategies/strategy-config.ts Updates fleet ship typing to new C4 create-fleet API
src/main/basedbot/fleet-strategies/mainnet-lu-strategy.ts Updates Game type to AccountWithKey wrapper
src/main/basedbot/fleet-strategies/mainnet-gellsn-strategy.ts Updates Game type to AccountWithKey wrapper
src/main/basedbot/fleet-strategies/get-fleet-strategy.ts Switches player identifier usage and threads existing fleets
src/main/basedbot/fleet-strategies/disband-all-strategy.ts Updates Game type to AccountWithKey wrapper
src/main/basedbot/fleet-strategies/destruct-all-strategy.ts Updates Game type to AccountWithKey wrapper
src/main/basedbot/fleet-strategies/atlasnet-qt-strategy.ts Uses game-derived R4 mint set and updated fleet selection signature
src/main/basedbot/fleet-strategies/atlasnet-lu-strategy.ts Updates Game type to AccountWithKey wrapper
src/main/airdrop/airdrop.ts Migrates airdrop logic to kit signers + C4 player context; adds faucet request helper
src/lib/telegram/telegram-bot.ts Removes telegram bot implementation
src/lib/telegram/response/wrong-param-count.ts Removes legacy telegram response helper
src/lib/telegram/response/unknown-wallet.ts Removes legacy telegram response helper
src/lib/telegram/response/unauthorized.ts Removes legacy telegram response helper
src/lib/telegram/response/index.ts Removes telegram response exports
src/lib/telegram/response/auth-pending.ts Removes legacy telegram response helper
src/lib/telegram/response/already-registered.ts Removes legacy telegram response helper
src/lib/telegram/middleware/params.ts Removes telegram params middleware
src/lib/telegram/middleware/index.ts Removes telegram middleware exports
src/lib/telegram/middleware/auth.ts Removes telegram auth middleware
src/lib/telegram/index.ts Removes telegram module exports
src/lib/telegram/context-message-update.ts Removes telegram context type
src/lib/telegram/commands/withdraw.ts Removes telegram command
src/lib/telegram/commands/verify.ts Removes telegram command
src/lib/telegram/commands/transactions.ts Removes telegram command
src/lib/telegram/commands/tip.ts Removes telegram command
src/lib/telegram/commands/support.ts Removes telegram command
src/lib/telegram/commands/stats.ts Removes telegram command
src/lib/telegram/commands/refills.ts Removes telegram command
src/lib/telegram/commands/refill.ts Removes telegram command
src/lib/telegram/commands/meme/wen.ts Removes telegram command
src/lib/telegram/commands/meme/porn.ts Removes telegram command
src/lib/telegram/commands/meme/kitten.ts Removes telegram command
src/lib/telegram/commands/meme/index.ts Removes telegram meme exports
src/lib/telegram/commands/logout.ts Removes telegram command
src/lib/telegram/commands/index.ts Removes telegram command exports
src/lib/telegram/commands/help.ts Removes telegram command
src/lib/telegram/commands/enable.ts Removes telegram command
src/lib/telegram/commands/enable-notify.ts Removes telegram command
src/lib/telegram/commands/disable.ts Removes telegram command
src/lib/telegram/commands/disable-notify.ts Removes telegram command
src/lib/refill.ts Removes legacy refill cron implementation
src/lib/refill-strategy/refill-strategy.ts Removes legacy refill strategy type
src/lib/refill-strategy/optimal-refill-strategy.ts Removes legacy refill strategy implementation
src/lib/refill-strategy/index.ts Removes legacy refill strategy exports
src/lib/refill-strategy/full-refill-strategy.ts Removes legacy refill strategy implementation
src/lib/refill-player.ts Removes legacy refill player implementation
src/lib/index.ts Removes legacy lib barrel exports
src/lib/get-price.ts Removes legacy price helper
src/lib/fleet-depletion-info.ts Removes legacy depletion helper
src/lib/ensure-wallet.ts Removes legacy wallet upsert helper
src/lib/const/max.ts Removes legacy Big.js helper
src/lib/const/index.ts Removes legacy const exports
src/lib/const/fleet-refill.ts Removes legacy refill const typing
src/lib/check-transactions.ts Removes legacy transaction scanning
src/lib/check-r4-transactions.ts Removes legacy R4 scanning
src/lib/check-atlas-transactions.ts Removes legacy ATLAS scanning
src/lib/airdrop.ts Removes legacy airdrop helper (replaced in main module)
src/db/entities/tx-cache.ts Keeps entity but relaxes transaction json typing
src/config/config.ts Removes mint envs and adds default faction/character config for C4
package.json Replaces dependencies with kit/dev-sage stack; updates scripts
eslint.config.mjs Ignores contrib directory
docker/Dockerfile Removes fleetbot artifact from image
contrib/solana-dev-skill Adds submodule reference for Solana development skill docs
.github/workflows/cicd.yml Passes NPM token into install/build steps
.github/actions/install/action.yml Adds optional npm token input and writes .npmrc for auth
.github/actions/docker-buildx/action.yml Threads npm token into docker build secrets
.cursor/rules/wait-for-instructions.mdc Adds Cursor rule doc
.cursor/rules/solana-dev.mdc Adds Cursor rule doc
.cursor/rules/reference-code.mdc Adds Cursor rule doc

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 13 to 23
const result = await rpcFetch({
jsonrpc: '2.0',
id: 1,
method: 'getRecentPrioritizationFees',
params: [[]],
})

const feeData = (result as any).result.result as Array<{
const feeData = (result as any).result as Array<{
slot: number
prioritizationFee: number
}>
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

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

This changes both the request shape and the response parsing for getRecentPrioritizationFees. Previously the code parsed result.result.result, and the params included at least one relevant account/program id; now it sends params: [[]] and reads result.result. Unless rpcFetch already unwraps envelopes, this is likely to break fee parsing and/or always fall back. Align the request/response with the actual JSON-RPC payload returned by rpcFetch, and include a meaningful account list (or correctly omit params) so the RPC can return non-empty fee estimates.

Copilot uses AI. Check for mistakes.
Comment on lines 77 to 80
const allFleets = await fetchAllAccounts('Fleet')
return allFleets.filter(
(f) => f.ownerProfile === player.character.playerProfile,
)
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

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

Fetching all Fleet accounts and filtering client-side will become very expensive on shared clusters. If solana-kite supports it, prefer server-side filtering (memcmp / indexed fields) or a dedicated query/factory that fetches only fleets owned by ownerProfile.

Copilot uses AI. Check for mistakes.
Comment on lines 69 to 75
if (typeof coord === 'object' && 'raw' in coord) {
const raw = (coord as RawCoord).raw
// Assume 56 fractional bits for I8F56 format
const fractionalBits = 56
const scale = 1n << BigInt(fractionalBits)
return Math.round(Number(raw) / Number(scale))
}
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

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

Converting raw and scale bigints to Number can lose precision (and can overflow for large magnitudes), leading to incorrect coordinate rounding—exactly what this helper is trying to avoid. Compute the integer part using bigint division first (e.g., raw / scale) and only then convert the quotient to number for display.

Copilot uses AI. Check for mistakes.
Comment on lines 99 to 102
role: item.attributes.spec as ShipRole,
mint: new PublicKey(item.mint),
mint: item.mint as Address,
make: item.attributes.make as ShipMake,
model: (item.attributes.model as ShipModel) || 'Unknown',
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

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

Casting item.mint to Address bypasses validation and can allow malformed/invalid addresses to flow into on-chain instruction building later. Prefer parsing/validating with address(item.mint) (or an equivalent safe constructor) and handling failures explicitly.

Copilot uses AI. Check for mistakes.
Comment on lines 130 to 134
await Promise.all(
config.app.airdropWallets.map((w) =>
airdropOrCreateProfile(new PublicKey(w)),
airdropOrCreateProfile(w as Address, keys),
),
)
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

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

Casting config values to Address skips validation; an invalid address string will either fail later in a less obvious way or be sent to the faucet incorrectly. Parse upfront with address(w) (and log/skip invalid entries) so failures are immediate and actionable.

Copilot uses AI. Check for mistakes.
mindrunner and others added 2 commits February 8, 2026 18:03
- Fix mine-biomass coordinate mismatch (resource lookup used wrong coords)
- Fix bigint precision loss in transform-sector (use bigint division before Number conversion)
- Fix priority-fee response parsing (correct rpcFetch envelope unwrapping, add fleet program to params)
- Fix init-keypair race condition (replace mutable export with async getKeyPair accessor)
- Fix fetchAccount perf (use fetchEncodedAccount for single getAccountInfo call instead of fetching all)
- Fix getUserFleets perf (use getProgramAccounts with memcmp filter on ownerProfile at offset 42)
- Guard isSecureContext assignment with typeof check + Object.defineProperty
- Validate addresses with address() instead of unsafe casts in ships.ts and airdrop.ts
- Fix pod-cleanup stub return type from never[] to unknown[]

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@mindrunner mindrunner requested a review from Copilot February 8, 2026 10:26
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 197 out of 200 changed files in this pull request and generated 10 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +49 to +57
const derivedKey = derivePath(path, seed.toString('hex')).key

logger.debug(`${path} => ${keypair.publicKey.toBase58()}`)
const signer = await createKeyPairSignerFromPrivateKeyBytes(derivedKey)

if (keypair.publicKey.equals(pubKey)) {
logger.info(`Found keypair for ${pubKey.toBase58()} at ${path}`)
logger.debug(`${path} => ${signer.address}`)

return keypair
if (signer.address === pubKey) {
logger.info(`Found keypair for ${pubKey} at ${path}`)
return signer
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

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

derivePath(...).key from ed25519-hd-key is typically a 32-byte seed (same shape that Keypair.fromSeed() expected). createKeyPairSignerFromPrivateKeyBytes() generally expects private key bytes (often 64-byte secret key), so this is very likely to derive the wrong address or throw. Prefer using a “from seed” constructor (if available in @solana/kit) or explicitly expand the 32-byte seed into the expected private-key format before creating the signer.

Copilot uses AI. Check for mistakes.
Comment on lines 21 to 39
const systems = await fetchAllAccounts('StarSystem')

return (
systems.find((system) => {
const sysCoords = system.coordinates as unknown as [
C4Coord,
C4Coord,
]
const sysX = Math.round(sysCoords[0].toNumber())
const sysY = Math.round(sysCoords[1].toNumber())
if (tolerance === 0) {
return sysX === coordinates.x && sysY === coordinates.y
}
// Fuzzy match within tolerance
const dx = Math.abs(sysX - coordinates.x)
const dy = Math.abs(sysY - coordinates.y)
return dx <= tolerance && dy <= tolerance
}) ?? null
)
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

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

Fetching all StarSystem accounts on every coordinate lookup will be a major bottleneck as the number of systems grows, especially since this is likely run frequently during FSM ticks. Consider introducing an in-memory cache/index keyed by (x,y) (and maybe a tolerance-aware lookup) that is built once per process (or periodically refreshed) rather than re-querying and re-scanning for each call.

Copilot uses AI. Check for mistakes.
Comment on lines +20 to +21
// Cached resources from game
let cachedResources: GameResources | null = null
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

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

cachedResources is global and not keyed by game.key (or cluster). If this process ever loads a different Game (tests, multiple environments, hot reload, etc.), getGameResources() can return mints for the wrong game. Cache should be keyed by game.key (or removed), or at least cleared when game.key changes.

Copilot uses AI. Check for mistakes.
Copy link
Owner Author

Choose a reason for hiding this comment

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

probably not an issue here. one bot won't run on multiple games / clusters at the same time.

Comment on lines +29 to +36
export function getGameResources(
game: AccountWithKey<Game>,
_fleet?: AccountWithKey<Fleet>,
): GameResources {
// Use cached if available
if (cachedResources) {
return cachedResources
}
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

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

cachedResources is global and not keyed by game.key (or cluster). If this process ever loads a different Game (tests, multiple environments, hot reload, etc.), getGameResources() can return mints for the wrong game. Cache should be keyed by game.key (or removed), or at least cleared when game.key changes.

Copilot uses AI. Check for mistakes.
Comment on lines 19 to 25
export const getCleanPodsByStarbasePlayerAccounts = async (
connection: Connection,
cargoProgram: CargoIDLProgram,
starbasePlayer: PublicKey,
_starbasePlayer: Address,
): Promise<PodCleanup | undefined> => {
const cargoPods = await getCargoPodsByAuthority(
connection,
cargoProgram,
starbasePlayer,
)
if (cargoPods.length > 1) {
let podsToClean = cargoPods.map((cargoPod) => {
if (cargoPod.type === 'error')
throw new Error('Error reading CargoPod account')
return cargoPod.data
})
const mainPod = podsToClean.reduce((prev, current) => {
return prev.data.openTokenAccounts > current.data.openTokenAccounts
? prev
: current
})
podsToClean = podsToClean.filter((it) => !it.key.equals(mainPod.key))
const podsAndTokensToClean: Array<[PublicKey, Account[]]> =
await Promise.all(
podsToClean.map(async (thisPod) => {
const podTokenAccounts =
await getParsedTokenAccountsByOwner(
connection,
thisPod.key,
)
return [thisPod.key, podTokenAccounts]
}),
)

return {
mainPod: mainPod.key,
podsAndTokensToClean,
cargoSeqId: mainPod.data.seqId,
}
}
// In C4, cargo pod handling may be different
// Return undefined to indicate no cleanup needed for now
return undefined
}
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

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

These functions preserve the old API surface but now silently no-op, which can hide functional regressions (callers will “succeed” without cleaning). If cleanup is intentionally disabled for C4, consider making this explicit via a feature flag in config, or throw a clear “Not implemented for C4” error so behavior is unambiguous in production.

Copilot uses AI. Check for mistakes.
Copy link
Owner Author

Choose a reason for hiding this comment

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

this can be removed entirely.

Comment on lines 31 to 36
export const getPodCleanupInstructions = (
podCleanup: PodCleanup,
sageProgram: SageIDLProgram,
cargoProgram: CargoIDLProgram,
starbasePlayer: PublicKey,
starbase: PublicKey,
playerProfile: PublicKey,
profileFaction: PublicKey,
cargoStatsDefinition: PublicKey,
gameId: PublicKey,
gameState: PublicKey,
key: AsyncSigner,
keyIndex: number,
) => {
const cleanInstructions: InstructionReturn[] = []
const newTokenAccounts: string[] = []
if (podCleanup.podsAndTokensToClean.length > 1) {
for (
let index2 = 0;
index2 < podCleanup.podsAndTokensToClean.length;
index2++
) {
const element = podCleanup.podsAndTokensToClean[index2]
const thisPodKey = element[0]
const podTokenAccounts = element[1]
if (podTokenAccounts.length > 0) {
for (
let index3 = 0;
index3 < podTokenAccounts.length;
index3++
) {
const tokenData = podTokenAccounts[index3]
const cargoType = CargoType.findAddress(
cargoProgram,
cargoStatsDefinition,
tokenData.mint,
podCleanup.cargoSeqId,
)[0]
if (Number(tokenData.delegatedAmount) > 0) {
const tokenTo = createAssociatedTokenAccountIdempotent(
tokenData.mint,
podCleanup.mainPod,
true,
TOKEN_PROGRAM_ID,
)
const tokenToBase58 = tokenTo.address.toBase58()
if (!newTokenAccounts.includes(tokenToBase58)) {
newTokenAccounts.push(tokenToBase58)
cleanInstructions.push(tokenTo.instructions)
}
const transferIx =
StarbasePlayer.transferCargoAtStarbase(
sageProgram,
cargoProgram,
starbasePlayer,
key,
'funder',
playerProfile,
profileFaction,
starbase,
thisPodKey,
podCleanup.mainPod,
cargoType,
cargoStatsDefinition,
tokenData.address,
tokenTo.address,
tokenData.mint,
gameId,
gameState,
{
amount: new BN(
tokenData.delegatedAmount.toString(),
),
keyIndex,
},
)
cleanInstructions.push(transferIx)
} else {
const closeIx =
StarbasePlayer.closeStarbaseCargoTokenAccount(
sageProgram,
cargoProgram,
starbasePlayer,
key,
'funder',
playerProfile,
profileFaction,
starbase,
thisPodKey,
cargoType,
cargoStatsDefinition,
tokenData.address,
tokenData.mint,
gameId,
gameState,
{
keyIndex,
},
)
cleanInstructions.push(closeIx)
}
}
}
cleanInstructions.push(
StarbasePlayer.removeCargoPod(
sageProgram,
cargoProgram,
starbasePlayer,
key,
playerProfile,
profileFaction,
'funder',
starbase,
thisPodKey,
gameId,
gameState,
{
keyIndex,
},
),
)
}
}
return cleanInstructions
_podCleanup: PodCleanup,
): unknown[] => {
// Return empty array - no cleanup instructions for now
return []
}
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

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

These functions preserve the old API surface but now silently no-op, which can hide functional regressions (callers will “succeed” without cleaning). If cleanup is intentionally disabled for C4, consider making this explicit via a feature flag in config, or throw a clear “Not implemented for C4” error so behavior is unambiguous in production.

Copilot uses AI. Check for mistakes.
Comment on lines 4 to 11
if (typeof (globalThis as any).isSecureContext === 'undefined') {
Object.defineProperty(globalThis, 'isSecureContext', {
value: true,
writable: false,
configurable: true,
enumerable: true,
})
}
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

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

Setting globalThis.isSecureContext = true globally can affect unrelated libraries/logic that use this flag for security-sensitive decisions. Prefer scoping the workaround as narrowly as possible (e.g., a dedicated polyfill module imported only by the specific entrypoints that need @staratlas/dev-sage, and/or set it to a value derived from runtime conditions rather than always true).

Copilot uses AI. Check for mistakes.
Copy link
Owner Author

Choose a reason for hiding this comment

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

afaik, this was solved upsteam already, but needs to be tested.

Comment on lines 36 to 40
export function getFuelNeeded(fleet: AccountWithKey<Fleet>): number {
const currentFuel = Number(fleet.fuelTank?.amount ?? 0n)
const maxFuel = Number(fleet.stats?.cargoStats?.fuelCapacity ?? 0)
return Math.max(0, maxFuel - currentFuel)
}
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

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

Converting bigint amounts to number can lose precision if values exceed Number.MAX_SAFE_INTEGER (and can produce incorrect deltas if capacities/amounts scale up). This computation is safer as bigint arithmetic end-to-end (return bigint needed), converting to number only at the final boundary where the downstream instruction strictly requires it.

Copilot uses AI. Check for mistakes.
Comment on lines 36 to 40
export function getAmmoNeeded(fleet: AccountWithKey<Fleet>): number {
const currentAmmo = Number(fleet.ammoBank?.amount ?? 0n)
const maxAmmo = Number(fleet.stats?.cargoStats?.ammoCapacity ?? 0)
return Math.max(0, maxAmmo - currentAmmo)
}
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

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

Same issue as refuel: converting bigint balances to number can truncate/overflow and miscompute the needed amount. Prefer bigint arithmetic and only coerce at the API boundary if required.

Copilot uses AI. Check for mistakes.
Comment on lines 9 to 11
export const createPriorityFeeInstruction = async (
instructions: TransactionInstruction[],
): Promise<TransactionInstruction> => {
const transaction = getDummyTransaction(instructions, keyPair.publicKey, [])
let encodedTx: string | undefined
try {
encodedTx = base58.encode(transaction.serialize())
} catch (e) {
logger.error((e as any).message)
}

_instructions: Instruction[],
): Promise<Instruction> => {
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

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

The function still accepts instructions but no longer uses them (it’s now only fetching fees scoped to fleetProgram). Consider removing this parameter (or renaming the function) to reflect current behavior, or re-introduce instruction-based scoping if callers expect the fee estimation to depend on the transaction content.

Copilot uses AI. Check for mistakes.
- Remove pod-cleanup.ts entirely (no consumers, no longer needed in C4)
- Remove unused _instructions param from createPriorityFeeInstruction
- Use bigint arithmetic in refuel/rearm to avoid precision loss on large values
- Cache StarSystem accounts in starbase-by-coordinates (5min TTL)
- Remove isSecureContext workaround (solved upstream in dev-sage)

Co-authored-by: Cursor <cursoragent@cursor.com>
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.

1 participant