Preparing wallet imports
Bringing a wallet asset into the game needs an in-game destination. A wallet balance and a Profile address alone do not establish that destination. Read the existing accounts first, create only the missing prerequisites, then load fresh snapshots before planning an import.
This guide describes the path checked through unsigned z.ink PTR simulations. Explicit Profile creation, Character registration, initial faction enrollment, and wallet cargo and ship imports each have separate planners. Simulation coverage does not establish a live creation or import result.
The accounts you need
Section titled “The accounts you need”| Account | What it establishes | How to check today |
|---|---|---|
| Profile | Your cross-game identity and authorized keys | getProfile from identity; it is signer-created, never derived from the wallet. |
| Character | That Profile’s presence in this particular Game | maybeGetCharacterForProfile from identity derives and validates the canonical Character. |
| Profile Faction | That Profile’s faction enrollment | maybeGetFactionForProfile from factions derives and validates it; only absence returns undefined. |
| Starbase Player | That Character’s player-local storage at a particular System’s nested Starbase | getStarbasePlayerForCharacterAtSystem from starbases derives and validates it; a missing account produces ACCOUNT_NOT_FOUND. |
These are separate accounts, with different scopes. Profile Faction belongs to the Profile. Character belongs to a Profile and Game. Starbase Player belongs to a Character and System. Reuse valid existing accounts; an account for a different Game, Profile, Character, or System is not a substitute.
The example below reads an existing Profile/Character chain. It does not register accounts or prove import readiness.
From an existing Profile to a local Starbase
Section titled “From an existing Profile to a local Starbase”- Read the Profile and its canonical Character. The normal
sage.characters.forProfile(profileAddress)call reads an existing Character; it does not create one. UsemaybeGetCharacterForProfilewhen absence is an expected onboarding state. - If Character registration is needed, its generated instruction takes a
separate account-storage funder. It does not take a Profile authorization
signer or a faction account. Use
planRegisterCharacterfrom@aephia/atlas-kit/identity/actions; read and reuse an existing Character instead of trying to register it again. New registration Plans tolerate unrelated Game definition updates. Execution still requires a valid C4 Game, unchanged Profile state, and an absent canonical Character. - Read enrollment with
maybeGetFactionForProfile. If absent, useplanChooseFaction(ctx, { faction, authorization })fromfactions/actions. Selectmud,oni, orusturexplicitly. The Profile authority signs; there is no separate funder. The Profile’s own on-chain balance must fund creation while preserving its current rent reserve. The planner validates observed funding against current Rent data but never tops up the Profile. Existing enrollment, includingunaligned, is rejected; this is not a faction-switching API. See faction enrollment. - Choose a System in the configured Game with a nested Starbase and suitable
current access. For wallet imports, select a permitted CSS. Use the existing
planRegisterStarbasePlayerwith loaded Character and System snapshots and an explicit funder if the local account is missing. The planner freshly validates faction and Starbase relationships; it rejects an already registered player, so reuse that account instead. In the capture, a MUD-enrolled Profile could not register at Eternity’s Ustur Starbase. Do not assume every nested Starbase accepts every faction. - Simulate a Plan, obtain the explicitly required signatures, execute once, and read the resulting account after confirmed success. Load fresh inputs before the next planner. Do not build later steps against accounts that only exist in an unexecuted Plan.
Character registration and faction enrollment are distinct prerequisites for this supported Starbase-registration path; Character creation itself does not require prior faction enrollment. If a Profile is missing, create and confirm it first using the separate planner below. Character registration and initial faction enrollment then use the focused planners described here and in the faction guide.
Plan Profile creation
Section titled “Plan Profile creation”Use planCreateProfile(ctx, profileAddress, options) from
@aephia/atlas-kit/identity/actions. The target must be a fresh on-curve address
for a new external signer, remain absent, and differ from the funder. The
planner reads the target to check absence without loading Game definitions.
Choose initial keys and keyThreshold explicitly; the SDK adds no default
permissions or authority.
This illustrative example grants one key only Profile AUTH. Supply the declared
addresses and context from your application. Use PROFILE_PERMISSION_SCOPE and the named authenticate permission
from the SDK identity entry; AUTH has that meaning only in this scope. This key alone does not establish permissions for SAGE gameplay actions.
import { planCreateProfile } from '@aephia/atlas-kit/identity/actions';import { composeProfilePermissions, PROFILE_PERMISSION_SCOPE,} from '@aephia/atlas-kit/identity';import type { Address, SageContext } from '@aephia/atlas-kit/client';
declare const ctx: SageContext;declare const profileAddress: Address;declare const funder: Address;declare const authority: Address;
const plan = await planCreateProfile(ctx, profileAddress, { funder, keys: [ { address: authority, scope: PROFILE_PERMISSION_SCOPE, permissionsRaw: composeProfilePermissions(PROFILE_PERMISSION_SCOPE, [ 'authenticate', ]), }, ], keyThreshold: 1,});console.log(plan.describe());Each initial key specifies an address, program scope, and exact unsigned
64-bit permissionsRaw bigint. Omitted expiresAt means -1n, nonexpiring.
Every key with AUTH bit 0 in the pinned Profile program scope must be
nonexpiring and must sign creation. Other scoped keys are readonly instruction
accounts and do not become signers merely by appearing in the key list.
Use permission and key inspection
to explain masks and inspect direct keys on a loaded Profile. These helpers
neither choose authority nor change the creation signer requirements. Separate
SAGE-scoped entries can use SAGE_PERMISSION_SCOPE and known names such as
moveFleet or doMining; see SAGE permission inspection.
A recognized gameplay mask is not proof of an action’s full authorization
requirements. SAGE manageGame at bit zero is not Profile AUTH and cannot
replace an initial Profile authority key.
Duplicate address-and-scope pairs are rejected because they could inflate the
AUTH count; the same address in distinct scopes is supported.
keyThreshold is an integer from 1 through the number of initial Profile AUTH
keys, at most 255. It governs protected Profile updates; individual gameplay
instructions have their own permission rules. All initial AUTH keys sign
creation even if the threshold is lower. The funder and new Profile also sign.
An AUTH key may share the funder or new Profile address; shared roles use the
same external signer. The Plan retains addresses, never signing keys.
The new Profile signer authorizes account creation. After creation, authority comes from the stored Profile key entries; the Profile address is not automatically AUTH. If that address is also an AUTH key, the same signer continues to serve that authority role. Do not discard it just because the creation transaction has completed.
Keep initial key lists small. The v0 transactions assembled by Atlas Kit can occupy at most 1,232 wire bytes. The number of keys that fits depends on unique addresses, AUTH signers, the fee payer, and other instructions, so the threshold’s upper bound is not a usable-key-count guarantee. Assemble and simulate the exact Plan before signing. There is no automatic chunking or curated later-key-addition planner in the shipped surface.
Inspect the Plan and simulate before execution. The funder pays for Profile account rent; transaction fees are separate and actual funding requirements remain chain-authoritative. Creation does not top up the Profile’s excess balance. In particular, later faction enrollment may need additional Profile funding. This planner does not register a Character, choose a faction, or complete onboarding.
If another transaction occupies the target after planning, the absence guard
fails before signing: refresh and inspect that account before continuing.
After confirmed success, call
getProfile(ctx, profileAddress, { refresh: true }) and verify the initial key
addresses, scopes, permissions, expiry, and threshold before planning the next
step. Unsigned simulation alone does not prove creation. Cases outside this
curated scope require the explicit raw bindings escape hatch and their own
reviewed validation.
Plan Character registration
Section titled “Plan Character registration”Current Game-2 limitation: this planner still omits the registration XP accounts. #616 tracks the fix and verification of account creation and funding. The example describes the API; it is not a qualified current-game registration route.
Supply the existing Profile address and an explicit funder. The planner derives and checks the canonical Character, freshly validates the Profile and Game, and produces an inspectable Plan. It does not choose a faction, fund the Profile, or submit a transaction. Registration fails if the Character already exists.
import { planRegisterCharacter } from '@aephia/atlas-kit/identity/actions';import { maybeGetCharacterForProfile } from '@aephia/atlas-kit/identity';import type { Address, SageContext } from '@aephia/atlas-kit/client';
declare const ctx: SageContext;declare const profileAddress: Address;declare const funder: Address;
const character = await maybeGetCharacterForProfile(ctx, profileAddress, { refresh: true,});if (character === undefined) { const plan = await planRegisterCharacter(ctx, profileAddress, { funder }); console.log(plan.describe());}Use the existing simulation and execution flow
with external signers when the caller wants to proceed. The funder pays for
Character storage; transaction fees are a separate assembly/execution concern.
Balances and account-creation costs remain chain-authoritative. If someone
registers the Character after planning, the Plan’s strict absence guard fails
before signing: refresh and reuse the existing account. After confirmed success,
read with getCharacterForProfile(ctx, profileAddress, { refresh: true }) and
verify its Profile and Game identity before continuing.
A Character records a Profile’s presence in a Game. Its existence alone does not establish that the Profile owner participated in registration or consented to another action. The sole-funder registration evidence applies to the pinned bindings and captured PTR deployment.
Cargo readiness
Section titled “Cargo readiness”Wallet cargo deposit
uses an existing Starbase Player at a Central Space Station (CSS). In the
captured PTR deployment, registration at a non-CSS Starbase succeeded but
Ammunition deposit there failed with NOT_AT_CENTRAL_SPACE_STATION. Registration
alone therefore does not prove import readiness. Confirm the selected System’s
nested Starbase is a CSS using system.starbase?.maxLevel.kind === 'css' on a
loaded System snapshot. The cargo deposit planner checks this before returning
a Plan. Simulate the actual import as well; current ownership and access checks
remain chain-authoritative.
The selected cargo must be importable under the current Game definition. The source token account must have the expected mint, token program, token owner, and sufficient raw balance. Profile authority and token-owner authority are separate roles.
Cargo backing is held in a Game-level cache vault; the player’s entitlement is recorded in the Starbase Player inventory. A vault balance alone does not prove that your player received cargo. After confirmed execution, check both the wallet token debit and the matching player inventory credit using fresh reads.
Ship readiness
Section titled “Ship readiness”Ship import also requires a CSS in the tested deployment: registration at the
neighboring non-CSS Starbase succeeded, but ship import then failed with
NOT_AT_CENTRAL_SPACE_STATION.
Use the ship import planner,
planDepositShipsToGame from fleets/actions. It moves wallet ship tokens into
Character-owned escrow and records the ship entitlement in Starbase Player’s
shipsInEscrow. Importing ships does not create a Fleet or add those ships to a
Fleet.
Do not infer that Starbase registration is unnecessary from the binding’s
optional starbasePlayer input. The asynchronous builder fills an omitted
input with a derived Starbase Player address. Supplying no input to that builder
is therefore different from leaving the instruction account absent. In the
captured PTR simulations, an absent account and the program-address placeholder
both failed. The supported path therefore requires an existing, correctly
registered Starbase Player for ships as well as cargo.
Select an explicit configuration ID whose mint matches the source tokens. The ship planner validates the source, Character-owned escrow, CSS relationships, Profile authority and separate token-owner role. Missing escrow ATA creation requires explicit payer consent. A new configuration entry uses the Profile’s excess balance for Starbase storage; increasing an existing entry does not grow storage. Verify wallet, token-escrow and player-entitlement deltas after confirmation. Cargo’s shared vault rules do not apply to ships.
What counts as ready
Section titled “What counts as ready”Readiness is an observation of current accounts, not a remembered “onboarding complete” flag. Use refresh reads when resuming or following a confirmed write.
- Profile must contain the intended scoped keys, permissions, expiry, and threshold.
- Character must point to the expected Profile and configured Game.
- Profile Faction must point to the expected Profile and selected faction.
- Starbase Player must point to the expected Profile, Character, and chosen System.
- The import source and destination must satisfy the selected asset’s rules.
Treat only ACCOUNT_NOT_FOUND as absence. An invalid owner, malformed account,
wrong relationship, transport failure, or permission rejection needs its own
remedy; it is not a reason to try creating another account.
The SDK does not silently enroll a faction, fund a Profile, choose an entry System, or submit transactions during a read. Existing registration and import Plans remain inspectable before signing. A submitted signature or a successful unsigned simulation is not proof of a committed result.
Evidence and limits
Section titled “Evidence and limits”The onboarding capture, Profile creation capture, and ship capture record the pinned bindings, deployed program fingerprints, simulation slots, rejected prerequisites and returned account state. They use unsigned PTR simulation with no live account creation or asset movement. They do not establish mainnet behavior or all faction access, permission and certificate variants.