Appearance
Read Identity and Faction
This page connects the two foundation programs used by SAGE examples:
- Player Profile stores player identity, keys, roles, and player names.
- Profile Faction stores which faction a profile is enlisted with.
The examples are read-only. They can be used against the SAGE C4 PTR on the z.ink testnet without signing a transaction.
Read this page as a chain:
txt
profile address
-> read the profile
-> derive and read the player name
-> derive and read the faction account
-> use those concepts when reading SAGE accountsInputs
You need a Player Profile account address.
ts
import { address } from '@solana/kit';
const profileAddress = address('PROFILE_ADDRESS_HERE');The profile address is not necessarily the wallet address. Treat it as a Star Atlas profile account unless the UI or an indexer has already resolved that relationship for you.
Wallet vs profile
A wallet signs. A profile stores Star Atlas identity and permissions. One wallet can be related to profile state, but the addresses are not automatically the same thing.
Fetch the profile
This reads the profile account and prints a few beginner-useful fields:
authKeyCount: how many auth keys are on the profilekeyThreshold: how many keys are needed for profile updatesnextSeqId: the next role sequence numbercreatedAt: when the profile was created, as an on-chain timestamp
ts
import { createSolanaRpc } from '@solana/kit';
import { fetchMaybeProfile } from '@staratlas/dev-player-profile';
const rpc = createSolanaRpc('https://testnet-rpc.z.ink');
const profile = await fetchMaybeProfile(rpc, profileAddress, {
commitment: 'confirmed'
});
if (!profile.exists) {
throw new Error(`Profile not found: ${profileAddress}`);
}
console.log({
authKeyCount: profile.data.authKeyCount,
keyThreshold: profile.data.keyThreshold,
roleSequence: profile.data.nextSeqId,
createdAt: profile.data.createdAt
});The profileKeys array contains keys, permission scopes, expiry times, and raw permission bytes. Decode permission intent with the package permission helpers instead of manually interpreting the bytes in application code.
Fetch the player name
PlayerName is a PDA derived from the profile address.
That means the account address is calculated from the profile address and a fixed seed. You do not have to know the player-name account in advance.
ts
import { fetchMaybePlayerNameFromSeeds } from '@staratlas/dev-player-profile';
const playerName = await fetchMaybePlayerNameFromSeeds(
rpc,
{ profile: profileAddress },
{ commitment: 'confirmed' }
);
if (playerName.exists) {
const name = new TextDecoder().decode(new Uint8Array(playerName.data.name));
console.log(name);
}The generated account stores the name as remaining bytes. Decode it at the edge and keep the raw account object available for debugging.
Fetch the faction account
ProfileFactionAccount is also a PDA derived from the profile address.
This account tells you which Star Atlas faction the profile is enlisted with.
ts
import {
Faction,
fetchMaybeProfileFactionAccountFromSeeds
} from '@staratlas/dev-profile-faction';
const profileFaction = await fetchMaybeProfileFactionAccountFromSeeds(
rpc,
{ profile: profileAddress },
{ commitment: 'confirmed' }
);
if (!profileFaction.exists) {
console.log('Profile has no faction account yet');
} else {
console.log({
profile: profileFaction.data.profile,
faction: Faction[profileFaction.data.faction],
bump: profileFaction.data.bump
});
}The generated Faction enum currently contains:
ts
enum Faction {
Unaligned,
Mud,
Oni,
Ustur
}In Star Atlas gameplay terms, choosing MUD, ONI, or Ustur is strategic identity, not a cosmetic preference. The KB treats major faction choice as account-bound and historically permanent, so apps should avoid presenting faction state as a casual toggle unless the current PTR flow explicitly says otherwise.
How this fits SAGE
SAGE fleet accounts include both ownerProfile and faction. That is the bridge from identity and faction state into the gameplay program.
In plain English: when you read a fleet, you can see which Star Atlas profile owns it and which faction it belongs to.
ts
import { fetchMaybeFleet } from '@staratlas/dev-sage';
const fleet = await fetchMaybeFleet(rpc, address('FLEET_ADDRESS_HERE'), {
commitment: 'confirmed'
});
if (fleet.exists) {
console.log({
ownerProfile: fleet.data.ownerProfile,
faction: Faction[fleet.data.faction],
label: fleet.data.fleetLabel
});
}Safety notes
Read examples can be run freely against PTR RPC. Do not turn these snippets into transaction builders until the docs also show:
- the signer or wallet that is expected to authorize the action
- which profile key or role grants permission
- which accounts are writable
- which assets, cargo, fleet state, or faction state can change
- a simulation step before asking a wallet to sign
