Skip to content

Claim Stake Placement Workflow

Claim-stake placement adds player infrastructure to a celestial body. It touches land-style production state, faction/profile authority, local starbase context, and rent.

Use this as the canonical review pattern for infrastructure-placement workflows: read world and local player context, build the generated account instruction, summarize rent and writable accounts, simulate, then show account-creation and domain diff targets.

In the C4 economy, claim stakes are production infrastructure on celestial bodies. They produce resources into limited local storage, and fleets must interact with individual stakes when transferring resources out, so placement review should include the body, production intent, storage expectation, and local fleet/starbase path.

What Placement Touches

ts
import { getPlaceClaimStakeInstanceWithHubInstructionAsync } from '@staratlas/dev-sage';

Instruction data:

FieldMeaning
keyIndexPlayer Profile key index authorizing the action.
claimStakeDefinitionIdClaim-stake definition to place.
hubBuildingIdPlanet- and family-matched hub building bundled with the stake.
initialRentAmountOptional initial rent deposit.

Core accounts:

AccountRole in the workflow
gameRoot SAGE game account.
profileValidationSignerWallet/profile signer.
profileValidationProfilePlayer Profile account.
profileFactionProfile Faction account.
claimStakeInstanceNew generated account signer for the placed instance.
systemAndStarbasePlayerSystemStar system context.
systemAndStarbasePlayerStarbasePlayerPlayer state at the starbase.
celestialBodyCelestial body receiving the claim stake.
characterSAGE Character for this profile/game.
currencyCacheCurrency configuration cache.
systemProgramSolana System Program for account creation.

1. Read the state

Read the celestial body, starbase player, and currency cache before building:

ts
import {
	fetchMaybeCelestialBody,
	fetchMaybeCurrencyConfigCache,
	fetchMaybeStarbasePlayer
} from '@staratlas/dev-sage';

const [celestialBody, starbasePlayer, currencyCache] = await Promise.all([
	fetchMaybeCelestialBody(rpc, celestialBodyAddress, {
		commitment: 'confirmed'
	}),
	fetchMaybeStarbasePlayer(rpc, starbasePlayerAddress, {
		commitment: 'confirmed'
	}),
	fetchMaybeCurrencyConfigCache(rpc, currencyCacheAddress, {
		commitment: 'confirmed'
	})
]);

if (!celestialBody.exists) throw new Error('Celestial body not found');
if (!starbasePlayer.exists) throw new Error('Starbase player not found');
if (!currencyCache.exists) throw new Error('Currency cache not found');

console.log({
	body: celestialBody.data.name,
	bodyType: celestialBody.data.celestialBodyType,
	currencyCache: currencyCache.address
});

The UI should also show the claim-stake definition, player location, rent deposit, and any cargo or resource requirements.

2. Build the instruction

ts
import { generateKeyPairSigner } from '@solana/kit';
import { getPlaceClaimStakeInstanceWithHubInstructionAsync } from '@staratlas/dev-sage';

const claimStakeInstance = await generateKeyPairSigner();

const instruction = await getPlaceClaimStakeInstanceWithHubInstructionAsync({
	game: gameAddress,
	profileValidationSigner: walletSigner,
	profileValidationProfile: profileAddress,
	profileFaction: profileFactionAddress,
	claimStakeInstance,
	systemAndStarbasePlayerSystem: systemAddress,
	systemAndStarbasePlayerStarbasePlayer: starbasePlayerAddress,
	celestialBody: celestialBodyAddress,
	character: characterAddress,
	currencyCache: currencyCacheAddress,
	claimStakeDefinitionId: 1,
	hubBuildingId: 4,
	initialRentAmount: 1_000_000n,
	keyIndex: 0
});

Use initialRentAmount: null when the flow intentionally starts without an initial rent deposit.

3. Review before signing

Show these items before a wallet signs:

  • Celestial body name and system.
  • Claim-stake definition id.
  • Hub building id.
  • New claim-stake instance address.
  • Player Profile and keyIndex.
  • Profile Faction value.
  • Starbase player context.
  • Initial rent amount.
  • Expected account creation, rent, cargo, and production effects.

4. Simulate the full transaction

Simulation should catch an invalid claim-stake definition id, missing generated-account signature, insufficient rent/currency context, stale celestial-body state, and profile/faction mismatches before signing.

ts
const simulation = await rpc
	.simulateTransaction(encodedTransactionBase64, {
		commitment: 'confirmed',
		encoding: 'base64',
		replaceRecentBlockhash: true,
		sigVerify: false
	})
	.send();

console.log({
	err: simulation.value.err,
	unitsConsumed: simulation.value.unitsConsumed,
	logs: simulation.value.logs
});

5. Diff targets

AccountExpected change
ClaimStakeInstanceNew account should exist and decode as ClaimStakeInstance.
CelestialBodyClaim-stake placement or capacity state may change.
StarbasePlayerLocal player state, cargo, or rent-related state may change.
CharacterProgression or counters may change.
CurrencyConfigCacheUsually config context; include it in review because rent uses currency config.

Use Transaction Review and Account Diffs for the generic simulation and diff pattern.