Claim stakes
A claim stake is a player’s claim on a celestial body: a hub to build infrastructure on, producing resources over time rather than in a single action.
Reading them
Section titled “Reading them”By owner, which is the usual direction:
const stakes = await character.claimStakes.all();Or by the body, when you want to know who holds a particular place:
const stakes = await sage.claimStakes.byBody(bodyAddress);Container and building catalogs
Section titled “Container and building catalogs”Read the available definition tables independently of any player’s placed stakes. Containers describe the Claim Stake itself; buildings describe its infrastructure:
import { getClaimStakeDefinition, listClaimStakeDefinitions, getClaimStakeBuildingDefinition, listClaimStakeBuildingDefinitions,} from '@aephia/atlas-kit/claim-stakes';
const containers = await listClaimStakeDefinitions(ctx);const buildings = await listClaimStakeBuildingDefinitions(ctx);const firstContainer = containers[0];if (firstContainer !== undefined) { const definition = await getClaimStakeDefinition(ctx, firstContainer.id); console.log(definition.name, definition.slots);}const firstBuilding = buildings[0];if (firstBuilding !== undefined) { const definition = await getClaimStakeBuildingDefinition( ctx, firstBuilding.id, ); console.log(definition.power, definition.resourceExtraction);}These are two independent catalogs with immutable definitions shared between list and by-id reads. Building production and extraction fields describe the configuration; read a placed stake for its production state. A catalog entry alone does not establish whether a player can build it on a particular stake. See Definition catalogs for loading both together, persistence, and section refresh behavior.
Lifecycle is a state, not a flag
Section titled “Lifecycle is a state, not a flag”A stake’s state.kind is one of design, active, or deactivated. Those are
genuinely different situations rather than degrees of the same one:
if (stake.state.kind === 'active') { // Producing. Other kinds are not.}Reading a stake and assuming it is producing is the mistake this shape exists to prevent.
Buildings and construction
Section titled “Buildings and construction”A stake is not one thing — it is a plot with infrastructure on it. The snapshot carries the whole layout:
const stakes = await character.claimStakes.all();const layouts = stakes.map((stake) => ({ buildings: stake.buildings.length, crew: stake.neededCrew, underConstruction: stake.constructionRemainingSeconds > 0n,}));buildings is what has been placed — extraction, processing, power, storage —
and each wants crew. Construction is live state: a stake can be active and
still have constructionRemainingSeconds left on recent changes.
What it produces
Section titled “What it produces”Production follows the same pattern as mining: rates against a clock, not a stored total.
const output = stake.resources.netProduction;const held = stake.resources.inventory;netProduction is per-resource rates (consumption nets against generation —
a processing chain can make a rate negative), inventory and capacity are
what the stake holds right now, and lastTickAtUnixSeconds anchors the clock
you project from.
Rent keeps it standing
Section titled “Rent keeps it standing”A stake occupies land and pays for it: rentBalanceRaw is what remains and
lastRentAtUnixSeconds when it was last settled. A dry balance eventually
means eviction, and coming back from that is a respawn, not a resume.
Instances have no derivable address
Section titled “Instances have no derivable address”Like crafting processes, claim stake instances cannot be computed from the
character and the body — the address is assigned at creation. That is why the
reads are byCharacter and byBody rather than a derivation, and why finding
one you have no reference to needs discovery.
Placing a claim stake
Section titled “Placing a claim stake”Placement turns the reads above into one planned transaction. If this would be your first write, read How Atlas Kit changes the game first.
The planner needs the Planet and Starbase Player snapshots you intend to use, the exact Claim Stake definition and zero-cost bundled hub ids from the current Game catalog, a positive raw initial rent amount, your Profile authorization, and a fresh address for the new Claim Stake. That address has no derivation, so generate its key before planning and retain the key only until execution:
import { generateKeyPairSigner } from '@solana/kit';import type { PlanAuthorization } from '@aephia/atlas-kit/claim-stakes/actions';import { planPlaceClaimStake } from '@aephia/atlas-kit/claim-stakes/actions';import { executePlan } from '@aephia/atlas-kit/planning';
const authorization = { profile: character.profile.address, authority: authoritySigner.address, keyIndex: 0,} satisfies PlanAuthorization;
const claimStakeKey = await generateKeyPairSigner();const plan = await planPlaceClaimStake(ctx, planet, starbasePlayer, { authorization, claimStake: claimStakeKey.address, claimStakeDefinitionId: 2, hubBuildingId: 21, initialRentAmount: 500_000_000n,});
console.log(plan.summary, plan.requiredSigners, plan.preconditions);
const result = await executePlan(ctx, plan, { feePayer: authoritySigner, signers: [claimStakeKey],});Planning is inert: it does not sign, submit, reserve a plot, or retain either
signer. Inspect the summary, accounts, and freshness preconditions before
passing the Plan to a wallet boundary. The Profile authority and fresh Claim
Stake key sign only during executePlan; afterwards, rediscover the instance
instead of trying to derive its address.
The planner rejects known stale identity, Game catalog, cargo, crew, tag, and plot-capacity inputs before signing. Final plot races, permissions, rent arithmetic, and program capacity remain chain-authoritative, so a locally valid Plan can still fail safely at execution and should never be submitted twice after an unknown result.
Planning a building design
Section titled “Planning a building design”An owned stake in active or design can plan one finite set of building
additions and removals. Building-design planning uses the loaded stake plus the
same Profile authorization; quantities are positive and each building id can
appear only once:
import { planClaimStakeBuildingChanges } from '@aephia/atlas-kit/claim-stakes/actions';
const buildingPlan = await planClaimStakeBuildingChanges(ctx, stake, { authorization, buildingChanges: [ { buildingId: 9, kind: 'add', quantity: 2 }, { buildingId: 4, kind: 'remove', quantity: 1 }, ],});
console.log( buildingPlan.summary, buildingPlan.requiredSigners, buildingPlan.preconditions,);This planner is also inert. It refreshes the Claim Stake, Game definitions, identity, Starbase controller, cargo, and crew relationships; then it rejects known stale sequences, invalid definitions, quantity underflow or overflow, incompatible tags, insufficient construction cargo, and an unrepresentable resulting design before signing. Rent synchronization and the final concurrent design-sequence race remain chain-authoritative.
Finalizing a building design
Section titled “Finalizing a building design”An owned stake can finalize only while its loaded and fresh state is design.
Finalization accepts the stake and the same Profile authorization, then produces
one inspectable instruction:
import { planFinalizeClaimStakeBuildingChanges } from '@aephia/atlas-kit/claim-stakes/actions';
const finalizePlan = await planFinalizeClaimStakeBuildingChanges(ctx, stake, { authorization,});
console.log( finalizePlan.summary, finalizePlan.requiredSigners, finalizePlan.preconditions,);The planner rejects active, deactivated, foreign, stale, mismatched, or known delinquent stakes locally. The program re-synchronizes rent at execution and keeps final crew/building validation chain-authoritative, so inspect and present the Plan immediately before authorization.
Cancelling a building design
Section titled “Cancelling a building design”An owned stake in design can discard its pending building changes and return
to active. Cancellation accepts the stake and the same Profile authorization:
import { planCancelClaimStakeBuildingChanges } from '@aephia/atlas-kit/claim-stakes/actions';
const cancelPlan = await planCancelClaimStakeBuildingChanges(ctx, stake, { authorization,});
console.log( cancelPlan.summary, cancelPlan.requiredSigners, cancelPlan.preconditions,);The planner rejects active, deactivated, foreign, stale, or mismatched identity graphs locally. It does not estimate a refund. The program re-synchronizes rent and performs the design-to-active transition at execution, so rent arithmetic and concurrent transitions remain chain-authoritative.
Topping up rent
Section titled “Topping up rent”An owned Claim Stake in active or design state can receive one exact raw
rent top-up. Pass raw ATLAS units as a positive bigint; do not pass a display
ATLAS number:
import { planTopUpClaimStakeRent } from '@aephia/atlas-kit/claim-stakes/actions';
const topUpPlan = await planTopUpClaimStakeRent(ctx, stake, { amount: 500_000_000n, authorization,});
console.log( topUpPlan.summary, topUpPlan.requiredSigners, topUpPlan.preconditions,);The planner validates the fresh owned stake, its canonical Character/Planet/System/Starbase Player graph, and the Character’s known raw ATLAS balance. It preserves the exact raw amount in the Plan description and does not estimate how much rent duration the amount purchases; permissions, rent arithmetic, and concurrent balance changes remain chain-authoritative.
Starting a Fleet transfer
Section titled “Starting a Fleet transfer”An owned idle Fleet can start one transfer to an owned active Claim Stake.
Provide exact raw cargo quantities in dense load and unload lists. Loading
moves cargo from the Claim Stake to the Fleet; unloading moves it from the Fleet
to the Claim Stake:
import { planStartClaimStakeFleetTransfer } from '@aephia/atlas-kit/claim-stakes/actions';
const transferPlan = await planStartClaimStakeFleetTransfer(ctx, fleet, stake, { authorization, load: [{ cargoId: 3, amount: 25n }], unload: [{ cargoId: 1, amount: 10n }],});
console.log( transferPlan.summary, transferPlan.requiredSigners, transferPlan.preconditions,);The planner verifies the shared owner and Game, the Fleet’s positive normalized
crew, the current Fleet/Claim Stake/Planet/System/Starbase Player graph, known
source balances, destination capacities, and an optional present
FleetCrewBinding. Starbase Player remains a readonly instruction account and is
guarded by a whole-account precondition; it is not included in the Plan’s
writable affected addresses. Cargo ids must be unique across both lists. The
chain remains authoritative for range, transfer duration, permissions, and
concurrent state changes. Normal exit remains a separate permissionless action.
Recovering a stuck Fleet transfer
Section titled “Recovering a stuck Fleet transfer”After the transfer lock expires, an owner can recover a Fleet only when the current Fleet and Claim Stake cargo snapshots prove that the queued transfer cannot execute exactly:
import { planRecoverClaimStakeFleetTransfer } from '@aephia/atlas-kit/claim-stakes/actions';
const recoveryPlan = await planRecoverClaimStakeFleetTransfer( ctx, fleet, stake, { atUnixSeconds: 1_700_000_100n, authorization, },);
console.log( recoveryPlan.summary, recoveryPlan.requiredSigners, recoveryPlan.preconditions,);The planner accepts source shortfalls, invalid queued cargo ids, or destination overflow. It rejects a currently executable transfer and directs the caller to the permissionless normal exit. Recovery returns the Fleet to idle without moving cargo or changing the active Claim Stake; it never infers closure or uses force-exit semantics.
Deconstructing a claim stake
Section titled “Deconstructing a claim stake”An owned stake can be deconstructed only while it is active and after every
player-funded building has been removed and finalized. The definition-compatible
zero-cost bundled hub remains because deconstruction discards it:
import { planDeconstructClaimStake } from '@aephia/atlas-kit/claim-stakes/actions';
const deconstructPlan = await planDeconstructClaimStake(ctx, stake, { authorization,});
console.log( deconstructPlan.summary, deconstructPlan.requiredSigners, deconstructPlan.preconditions,);The planner derives the exact Planet chronology index and current Starbase plot level from fresh validated Game, Claim Stake, Body, System, Character, and Starbase Player accounts; callers cannot assert either value. It does not predict rent settlement, returned cargo, an account-close outcome, or a refund amount. Those results and any concurrent close race remain chain-authoritative.
Gotchas
Section titled “Gotchas”A stake in design is not yet producing. It exists, it is yours, and it
yields nothing. Filter on state before summing output.
Capacity and yield come from definitions. What a stake produces depends on the body and the Game account’s rules, not on fields stored in the stake account alone.
A body can hold stakes from several players. byBody returns an array for
that reason.
Reference
Section titled “Reference”claim-stakes— every export in this entryclaim-stakes/actions— Claim Stake placement, building-design planning, rent top-up, Fleet-transfer start and recovery, finalization, cancellation, and deconstructionworld— the bodies stakes are placed on- Crafting habs — the sibling deployment system, sharing the same lifecycle and rent machinery