Player Profile
@staratlas/player-profile
Introduction
Welcome to the Player Profile, brought to you by Star Atlas! This open protocol empowers developers to seamlessly create and manage decentralized profiles on the Solana blockchain. Dive into a world where game and application profiles are not bound by central control, ensuring greater trust, transparency, and user autonomy.
The Player Profile Program allows users to manage their identity and access keys. Users can create a list of public keys associated with their profile and set permissions for each key. Identity is no longer tied to a key and is instead tied to a profile, allowing for key rotation, complex ownership structures, and more. No CPI is required to check a key that signs on behalf of a profile.
Installation
npm i @staratlas/player-profileExamples
This guide provides a detailed overview and step‑by‑step instructions on using the player_profile program. It covers program details, environment setup, sample scripts, instruction usage, account structures, error definitions, key constants, and PDA derivations.
──────────────────────────────
1. Program Details
Program Name:
player_profile
Version:
0.7.3
Program Address: • pprofELXjL5Kck7Jn5hCpwAL82DpTkSYBENzahVtbc9
Description:
The player_profile program manages a complex on‑chain identity system. It decouples identity from signing keys by using a profile that stores multiple keys with associated permissions. Typical use cases include key rotation, assigning different roles to players, and enforcing multi‑signature authority over profile changes. The program further supports role creation and membership management, making it ideal for decentralized applications (DApps) where flexible identity management is required.
IDL Reference:
For a complete specification, please see the attached IDL file:
──────────────────────────────
2. Environment Setup & Loading the Program
Required Dependencies
Anchor CLI: Version ≥ 0.24 (or as specified in your workspace)
Solana CLI: Latest stable version recommended
Sample TypeScript Code for Setup
Below is a sample TypeScript snippet that imports the required dependencies, sets up the Anchor provider, and loads the program:
import * as anchor from '@project-serum/anchor';
import { Program, Provider, web3 } from '@project-serum/anchor';
import { player_profile, IDL } from './player_profile'; // Adjust the path to your generated IDL
// Set the provider. Ensure that your environment's Solana config is properly configured.
const provider = Provider.env();
anchor.setProvider(provider);
// Load the program from the workspace using the IDL and program address.
const programID = new web3.PublicKey("pprofELXjL5Kck7Jn5hCpwAL82DpTkSYBENzahVtbc9");
const program = new Program<IDL>(IDL, programID, provider);
// Now you can use `program` to call instructions and fetch on‑chain data.
──────────────────────────────
3. Sample Scripts/Quick Start
Below are examples showing how to fetch on‑chain data using TypeScript.
Fetch a Profile Account
async function fetchProfile(profilePubkey: web3.PublicKey) {
// Fetch the on‑chain account data for a profile
try {
const profileAccount = await program.account.profile.fetch(profilePubkey);
console.log("Profile:", profileAccount);
} catch (e) {
console.error("Error fetching profile account:", e);
}
}
Set a Profile Name
async function setProfileName(profilePubkey: web3.PublicKey, keyIndex: number, newName: string) {
// Derive PlayerName PDA using the profile pubkey (see PDA section below)
const [playerNamePda, nameBump] = await web3.PublicKey.findProgramAddress(
[Buffer.from("player_name"), profilePubkey.toBuffer()],
program.programId
);
// Call setName instruction with required accounts and parameter
await program.rpc.setName(new anchor.BN(keyIndex), Buffer.from(newName), {
accounts: {
key: /* the signer authorized with CHANGE_NAME permission */,
funder: provider.wallet.publicKey,
profile: profilePubkey,
name: playerNamePda,
systemProgram: web3.SystemProgram.programId,
},
signers: [/* include additional signer(s) as required */],
});
console.log(`Set profile name to: ${newName}`);
}
──────────────────────────────
4. Detailed Instruction Guide
For each instruction defined in the IDL, we briefly describe the purpose, key accounts, arguments, and provide a TypeScript invocation example.
4.1 acceptRoleInvitation
Purpose:
Enables a new member to accept an invitation to join a role. The invitation status is switched from inactive to active.
Key Accounts:
newMember (non‑mutable; not a signer unless it is a wallet)
roleAccount (mutable; the role being joined)
roleMembershipAccount (mutable; the membership record for the new member)
Arguments:
keyIndex (u16)
keyIndexInRoleAccount (u16)
keyIndexInMembershipAccount (u16)
TypeScript Example:
await program.rpc.acceptRoleInvitation(
new anchor.BN(keyIndex),
new anchor.BN(keyIndexInRoleAccount),
new anchor.BN(keyIndexInMembershipAccount),
{
accounts: {
newMember: newMemberPubkey,
roleAccount: roleAccountPubkey,
roleMembershipAccount: roleMembershipPubkey,
},
}
);
4.2 addExistingMemberToRole
Purpose:
Adds an existing member to a role via their membership account.
Key Accounts:
funder (payer; mutable, signer)
newMember (the account of the member being added)
profile (the profile associated with the role)
roleMembershipAccount (mutable membership account for the member)
roleAccount (mutable; the role to add a member to)
systemProgram
Arguments:
keyIndex (u16)
keyIndexInMembershipAccount (u16)
TypeScript Example:
await program.rpc.addExistingMemberToRole(new anchor.BN(keyIndex), new anchor.BN(keyIndexInMembershipAccount), {
accounts: {
funder: provider.wallet.publicKey,
newMember: newMemberPubkey,
profile: profilePubkey,
roleMembershipAccount: roleMembershipPubkey,
roleAccount: roleAccountPubkey,
systemProgram: web3.SystemProgram.programId,
},
});
4.3 addKeys
Purpose:
Adds new keys (with non‑auth permissions) to a profile using a key with ADD_KEYS permission.
Key Accounts:
funder (mutable; signer)
key (signer with ADD_KEYS permission)
profile (mutable; profile being updated)
systemProgram
(Additional remaining accounts: new key accounts to be added)
Arguments:
keyAddIndex (u16)
keyPermissionsIndex (u16)
keysToAdd (Vec<AddKeyInput>)
TypeScript Example:
await program.rpc.addKeys(new anchor.BN(keyAddIndex), new anchor.BN(keyPermissionsIndex), keysToAddArray, {
accounts: {
funder: provider.wallet.publicKey,
key: signerWithAddKeysPermission.publicKey,
profile: profilePubkey,
systemProgram: web3.SystemProgram.programId,
},
remainingAccounts: additionalKeyAccountMetas,
signers: [signerWithAddKeysPermission],
});
Note: The AddKeyInput structure must be constructed with fields “scope” (PublicKey), “expireTime” (i64) and “permissions” ([u8;8]).
4.4 adjustAuth
Purpose:
Adjusts the list of authentication keys and threshold. It allows one to add new auth keys and remove existing ones while ensuring the threshold is met.
Key Accounts:
funder (mutable; signer)
profile (mutable; profile to update)
systemProgram
(Remaining accounts: auth key accounts and new auth keys)
Arguments:
authIndexes (Vec<u16>)
newKeyPermissions (Vec<AddKeyInput>)
removeRange ([u16; 2])
newKeyThreshold (u8)
TypeScript Example:
await program.rpc.adjustAuth(
authIndexesArray,
newKeyPermissionsArray,
removeRangeArray,
new anchor.BN(newKeyThreshold),
{
accounts: {
funder: provider.wallet.publicKey,
profile: profilePubkey,
systemProgram: web3.SystemProgram.programId,
},
remainingAccounts: authKeyAccounts, // provide remaining auth accounts
}
);
4.5 createProfile
Purpose:
Creates a new player profile and initializes its key list based on the provided keys and threshold.
Key Accounts:
funder (mutable; payer and signer)
profile (mutable; profile being created; must be a signer account)
systemProgram
(Remaining accounts: key accounts used to initialize the profile)
Arguments:
keyPermissions (Vec<AddKeyInput>)
keyThreshold (u8)
TypeScript Example:
await program.rpc.createProfile(
keyPermissionsArray,
new anchor.BN(keyThreshold),
{
accounts: {
funder: provider.wallet.publicKey,
profile: profilePubkey, // new profile account pubkey
systemProgram: web3.SystemProgram.programId,
},
remainingAccounts: initialKeyMetas,
signers: [/* include profile keypair and any other required signers */],
}
);
4.6 createRole
Purpose:
Creates a new role for the given profile. The role can later be used to manage group-based permissions.
Key Accounts:
funder (mutable; signer)
profile (mutable; the account that owns the role)
newRoleAccount (mutable; role account to be created, derived via a PDA)
systemProgram
(Remaining accounts: key with CREATE_ROLE permission)
Arguments:
keyIndex (u16)
TypeScript Example:
await program.rpc.createRole(new anchor.BN(keyIndex), {
accounts: {
funder: provider.wallet.publicKey,
profile: profilePubkey,
newRoleAccount: rolePda, // derived PDA for the new role
systemProgram: web3.SystemProgram.programId,
},
remainingAccounts: [createRoleSignerMeta], // include any signing account if needed
signers: [createRoleSigner],
});
4.7 inviteMemberToRole
Purpose:
Invites a new member to a role by creating a new role membership record in an inactive state.
Key Accounts:
funder (mutable; signer)
newMember (account of the user to be invited)
profile (non‑mutable; profile for the role)
roleMembershipAccount (mutable; new membership record account)
roleAccount (mutable; the role being joined)
systemProgram
Arguments:
keyIndex (u16)
TypeScript Example:
await program.rpc.inviteMemberToRole(new anchor.BN(keyIndex), {
accounts: {
funder: provider.wallet.publicKey,
newMember: newMemberPubkey,
profile: profilePubkey,
roleMembershipAccount: membershipPda, // derived PDA for role membership
roleAccount: roleAccountPubkey,
systemProgram: web3.SystemProgram.programId,
},
signers: [provider.wallet.payer],
});
4.8 joinRole
Purpose:
Allows a member to accept an invitation and join a role. This instruction creates a new role membership record and adds the member to the role.
Key Accounts:
funder (mutable; signer)
newMember (account of the joining member)
roleMembershipAccount (mutable; new membership record account)
roleAccount (mutable; role account to be updated)
systemProgram
Arguments:
keyIndex (u16)
TypeScript Example:
await program.rpc.joinRole(new anchor.BN(keyIndex), {
accounts: {
funder: provider.wallet.publicKey,
newMember: newMemberPubkey,
roleMembershipAccount: membershipPda,
roleAccount: roleAccountPubkey,
systemProgram: web3.SystemProgram.programId,
},
signers: [provider.wallet.payer],
});
4.9 leaveRole
Purpose:
Permits a member to leave a role. The instruction removes the member from both the role account and the membership account, closing the latter if not needed.
Key Accounts:
funder (mutable; receives rent benefits)
member (the leaving member’s account)
roleMembershipAccount (mutable; membership record to update/close)
roleAccount (mutable; role account from which the member is removed)
systemProgram
Arguments:
keyIndex (u16)
keyIndexInRoleAccount (u16)
keyIndexInMembershipAccount (u16)
TypeScript Example:
await program.rpc.leaveRole(
new anchor.BN(keyIndex),
new anchor.BN(keyIndexInRoleAccount),
new anchor.BN(keyIndexInMembershipAccount),
{
accounts: {
funder: provider.wallet.publicKey,
member: memberPubkey,
roleMembershipAccount: membershipPda,
roleAccount: roleAccountPubkey,
systemProgram: web3.SystemProgram.programId,
},
signers: [/* member must sign if it’s a wallet */],
}
);
4.10 removeKeys
Purpose:
Removes non‑auth keys from a profile. It verifies that keys being removed are not the signer themselves and that auth keys cannot be removed using this instruction.
Key Accounts:
funder (mutable; receiver of reclaimed rent)
key (signer with REMOVE_KEYS permission)
profile (mutable; profile to update)
systemProgram
(No extra remaining accounts unless used for additional key checks)
Arguments:
keyIndex (u16)
keysToRemove (array [u16; 2]) – the start and end indexes of keys to be removed
TypeScript Example:
await program.rpc.removeKeys(
new anchor.BN(keyIndex),
[new anchor.BN(startIndex), new anchor.BN(endIndex)],
{
accounts: {
funder: provider.wallet.publicKey,
key: signerWithRemoveKeys.publicKey,
profile: profilePubkey,
systemProgram: web3.SystemProgram.programId,
},
signers: [signerWithRemoveKeys],
}
);
4.11 removeMemberFromRole
Purpose:
Removes a member from a role—this is initiated by an authorized key. Both the role’s membership list and the member’s role membership account are updated.
Key Accounts:
funder (mutable; receives rent benefits)
member (the member being removed)
profile (non‑mutable; required for validation)
roleMembershipAccount (mutable; membership record to update)
roleAccount (mutable; role from which the member is removed)
systemProgram
Arguments:
keyIndex (u16)
keyIndexInRoleAccount (u16)
keyIndexInMembershipAccount (u16)
TypeScript Example:
await program.rpc.removeMemberFromRole(
new anchor.BN(keyIndex),
new anchor.BN(keyIndexInRoleAccount),
new anchor.BN(keyIndexInMembershipAccount),
{
accounts: {
funder: provider.wallet.publicKey,
member: memberPubkey,
profile: profilePubkey,
roleMembershipAccount: membershipPda,
roleAccount: roleAccountPubkey,
systemProgram: web3.SystemProgram.programId,
},
}
);
4.12 removeRole
Purpose:
Removes a role from a profile. The instruction will close the role (and its name account if it exists) provided the role has no active members.
Key Accounts:
funder (mutable; receives reclaimed rent)
profile (mutable; associated profile)
roleAccount (mutable; role to be removed)
roleNameAccount (mutable; optional account storing the role’s name; closed upon removal)
Arguments:
roleNameBump (u8)
keyIndex (u16)
TypeScript Example:
await program.rpc.removeRole(new anchor.BN(roleNameBump), new anchor.BN(keyIndex), {
accounts: {
funder: provider.wallet.publicKey,
profile: profilePubkey,
roleAccount: roleAccountPubkey,
roleNameAccount: roleNamePda, // derived using roleAccount key and bump
},
});
4.13 setName
Purpose:
Sets or updates the profile’s name using a designated name account. The instruction may initialize the account if needed.
Key Accounts:
key (signer with CHANGE_NAME permission)
funder (mutable; pays for rent changes)
profile (non‑mutable; the profile)
name (mutable; the name account which is either pre‑initialized or created on‑the‑fly)
systemProgram
Arguments:
keyIndex (u16)
name (bytes – typically a UTF‑8 encoded string)
TypeScript Example:
await program.rpc.setName(new anchor.BN(keyIndex), Buffer.from("Hello!"), {
accounts: {
key: signerWithChangeName.publicKey,
funder: provider.wallet.publicKey,
profile: profilePubkey,
name: playerNamePda, // derived as shown in the PDA section
systemProgram: web3.SystemProgram.programId,
},
signers: [signerWithChangeName],
});
4.14 setRoleAcceptingMembers
Purpose:
Flags the role to accept new members.
Key Accounts:
profile (non‑mutable)
roleAccount (mutable; role to update)
Arguments:
keyIndex (u16)
TypeScript Example:
await program.rpc.setRoleAcceptingMembers(new anchor.BN(keyIndex), {
accounts: {
profile: profilePubkey,
roleAccount: roleAccountPubkey,
},
});
4.15 setRoleAuthorizer
Purpose:
Sets the authorizer field on a role if it has not already been set.
Key Accounts:
funder (mutable; signer)
profile (non‑mutable)
roleAccount (mutable; role to be updated)
authorizer (non‑mutable; account to be assigned as authorizer)
Arguments:
keyIndex (u16)
TypeScript Example:
await program.rpc.setRoleAuthorizer(new anchor.BN(keyIndex), {
accounts: {
funder: provider.wallet.publicKey,
profile: profilePubkey,
roleAccount: roleAccountPubkey,
authorizer: authorizerPubkey,
},
signers: [provider.wallet.payer],
});
4.16 setRoleName
Purpose:
Sets or updates the name of a role using a separate name account.
Key Accounts:
funder (mutable; signer)
profile (non‑mutable; profile owning the role)
role (non‑mutable; the role to update name for)
name (mutable; name account holding the role name)
systemProgram
Arguments:
keyIndex (u16)
name (bytes – role name as UTF‑8 encoded string)
TypeScript Example:
await program.rpc.setRoleName(new anchor.BN(keyIndex), Buffer.from("Commander"), {
accounts: {
funder: provider.wallet.publicKey,
profile: profilePubkey,
role: roleAccountPubkey,
name: roleNamePda, // derived PDA for role name account
systemProgram: web3.SystemProgram.programId,
},
signers: [provider.wallet.payer],
});
4.17 setRoleNotAcceptingMembers
Purpose:
Flags a role so that it stops accepting new members.
Key Accounts:
profile (non‑mutable)
roleAccount (mutable; role to update)
Arguments:
keyIndex (u16)
TypeScript Example:
await program.rpc.setRoleNotAcceptingMembers(new anchor.BN(keyIndex), {
accounts: {
profile: profilePubkey,
roleAccount: roleAccountPubkey,
},
});
──────────────────────────────
5. Key Accounts & Structures
Below are the most important data structures along with definitions of their fields.
Profile Account
Purpose:
Stores the core profile information including version, key threshold, authentication key count, next role sequence ID, and creation timestamp.
Data Fields:
version: u8– Data version.authKeyCount: u16– Number of authentication keys.keyThreshold: u8– Minimum number of auth keys required for sensitive operations.nextSeqId: u64– Sequence counter for new roles.createdAt: i64– Unix timestamp of creation.
PlayerName Account
Purpose:
Holds a human‑readable name for a profile or role.
Data Fields:
version: u8– Data version.profile: PublicKey– The profile’s public key this name is associated with.bump: u8– PDA bump seed.
PDA Derivation:
const [playerNamePda, bump] = await web3.PublicKey.findProgramAddress(
[Buffer.from("player_name"), profilePubkey.toBuffer()],
program.programId
);
ProfileRoleMembership Account
Purpose:
Manages a player’s membership to various roles.
Data Fields:
version: u8– Data version.profile: PublicKey– Profile identifier.member: PublicKey– Member’s profile public key.bump: u8– PDA bump seed.
PDA Derivation:
Typically derived from seeds such as ["role-member", profilePubkey, memberPubkey].
Role Account
Purpose:
Represents a role that is associated with a profile. It maintains an unordered list of role membership entries in its remaining data.
Data Fields:
version: u8– Data version.profile: PublicKey– The owning profile.authorizer: PublicKey– (Optional) Authority for role modifications.roleSeqId: u64– Sequence identifier for the role.acceptingNewMembers: u8– Boolean indicator (1 = true, 0 = false).bump: u8– PDA bump seed.
PDA Derivation Example:
const [rolePda, roleBump] = await web3.PublicKey.findProgramAddress(
[
Buffer.from("profile-role"),
profilePubkey.toBuffer(),
new anchor.BN(roleSeqId).toArrayLike(Buffer, "le", 8)
],
program.programId
);
ProfileKey Structure
Purpose:
Defines an individual key entry along with its permissions, scope, and expiry information.
Data Fields:
key: PublicKey– The public key.scope: PublicKey– Defines the operational scope.expireTime: i64– Expiration time (non‑expiring if negative).permissions: [u8;8]– Bitflag‐encoded permissions.
RoleMembership Structure
Purpose:
Stores a member’s status in a role.
Data Fields:
key: PublicKey– The key/identifier of the member.status: u8– Membership status (0 = Inactive, 1 = Active).
──────────────────────────────
6. Program Errors
Below is a complete table of error codes, names, and messages as defined in the program:
6000
KeyIndexOutOfBounds
Key index out of bounds
6001
ProfileMismatch
Profile did not match profile key
6002
KeyMismatch
Key did not match profile key
6003
ScopeMismatch
Scope did not match profile scope
6004
KeyExpired
Key expired
6005
KeyMissingPermissions
Key is missing permissions
6006
PermissionsMismatch
Permissions don't match available
6007
AuthKeyCannotExpire
Auth keys cannot expire
6008
AuthKeyMustSign
New auth keys must be signers
6009
DuplicateAuthKey
Duplicate key when adjusting auth keys
6010
RoleAuthorityAlreadySet
Role authority has already been set
6011
RoleNotAcceptingMembers
Role is not accepting new members
6012
RoleMembershipMismatch
Role membership is not as expected
6013
RoleLimitExceeded
Role limit exceeded
6014
RoleHasMembers
Cannot remove role with members
6015
FeatureNotImplemented
This feature is not yet support
──────────────────────────────
7. Key Constants
Some of the important constants include:
MAX_MEMBERSHIPS: 256
Significance: Maximum members allowed per role.
Permission Bitflags (ProfilePermissions):
Permissions such as AUTH (1 << 0), ADD_KEYS (1 << 1), REMOVE_KEYS (1 << 2), CHANGE_NAME (1 << 3), etc.
Significance: These define what actions a key may perform relative to a profile or role.
──────────────────────────────
8. PDAs (Program Derived Addresses)
Many accounts in the player_profile program are derived using PDAs. Below are sample derivations:
8.1 Deriving PlayerName PDA
Seed Values:
Literal: "player_name"
Profile public key
Sample Code:
const [playerNamePda, bump] = await web3.PublicKey.findProgramAddress(
[Buffer.from("player_name"), profilePubkey.toBuffer()],
program.programId
);
8.2 Deriving Role PDA
Seed Values:
Literal: "profile-role"
Profile public key
Role sequence ID (as little‑endian byte array)
Sample Code:
const roleSeqId = 0; // or the current value from the profile account
const [rolePda, roleBump] = await web3.PublicKey.findProgramAddress(
[
Buffer.from("profile-role"),
profilePubkey.toBuffer(),
new anchor.BN(roleSeqId).toArrayLike(Buffer, "le", 8)
],
program.programId
);
8.3 Deriving RoleMembership PDA
Often derived with a combination of a discriminator (e.g., "role-member"), the profile key, and the member key. Refer to the program’s seed definitions for details.
──────────────────────────────
9. Final Remarks
Helpful Hints:
• Use the provided TypeScript snippets as a starting point for your integration.
• Make use of the error table for debugging any on‑chain issues that arise during development.
This documentation should help you quickly integrate and interact with the player_profile program using its IDL and standard Anchor-based workflows. For any further clarifications, refer to the attached IDL (player_profile.json) or check additional inline comments in the program source.
Last updated
Was this helpful?