carbon-player-profile-decoder 0.12.1

Rust decoder for Star Atlas Player Profile program on Solana
Documentation
# Carbon Player Profile Decoder

<p align="center">
  <a href="https://crates.io/crates/carbon-player-profile-decoder">
    <img src="https://img.shields.io/crates/v/carbon-player-profile-decoder?logo=rust" />
  </a>
  <a href="https://docs.rs/carbon-player-profile-decoder">
    <img src="https://img.shields.io/docsrs/carbon-player-profile-decoder?logo=docsdotrs" />
  </a>
  <a href="https://github.com/staratlasmeta/star-atlas-decoders/blob/main/LICENSE">
    <img src="https://img.shields.io/badge/license-Apache%202.0-blue" />
  </a>
</p>

Rust decoder for the Star Atlas Player Profile program on Solana, generated using [Carbon CLI](https://github.com/sevenlabs-hq/carbon).

## Program Information

- **Program ID**: `pprofELXjL5Kck7Jn5hCpwAL82DpTkSYBENzahVtbc9`
- **Network**: Solana Mainnet
- **Description**: Star Atlas Player Profile program for managing player identities, permissions, and role-based access control within the Star Atlas ecosystem.

## Features

- Decodes all Player Profile account types
- Full instruction parsing support
- Integration with Carbon indexing framework
- Permission bitflags support for ergonomic permission checking
- Helper methods for key expiration and authorization checks

## Usage

Add this crate to your `Cargo.toml`:

```toml
[dependencies]
carbon-player-profile-decoder = "0.12.0"
```

### Decoding Accounts

```rust
use carbon_player_profile_decoder::{PlayerProfileDecoder, PlayerProfileAccount};
use carbon_core::account::AccountDecoder;

let decoder = PlayerProfileDecoder;
let decoded_account = decoder.decode_account(&account);

if let Some(decoded) = decoded_account {
    match decoded.data {
        PlayerProfileAccount::Profile(profile) => {
            println!("Profile has {} keys", profile.profile_keys.len());
            for key in &profile.profile_keys {
                println!("  Key: {}, Scope: {}", key.key, key.scope);
            }
        }
        PlayerProfileAccount::PlayerName(player_name) => {
            let name_str = String::from_utf8_lossy(&player_name.name);
            println!("Player Name: {}", name_str);
        }
        PlayerProfileAccount::Role(role) => {
            println!("Role has {} members", role.members.len());
            for member in &role.members {
                println!("  Member: {}", member.key);
            }
        }
        PlayerProfileAccount::ProfileRoleMembership(membership) => {
            println!("Profile has {} role memberships", membership.memberships.len());
        }
    }
}
```

### Decoding Instructions

The decoder supports parsing all Player Profile instructions with full account resolution:

```rust
use carbon_player_profile_decoder::{PlayerProfileDecoder, PlayerProfileInstruction};
use carbon_core::instruction::InstructionDecoder;

let decoder = PlayerProfileDecoder;
let decoded_ix = decoder.decode_instruction(&instruction);

if let Some(decoded) = decoded_ix {
    match decoded.data {
        PlayerProfileInstruction::AddKeys(add_keys) => {
            println!("Adding {} keys to profile", add_keys.keys_to_add.len());

            // Access the instruction data (permissions)
            for (i, key_input) in add_keys.keys_to_add.iter().enumerate() {
                println!("  Key {}: scope={}, expire={}",
                    i, key_input.scope, key_input.expire_time);
            }

            // Access the actual account pubkeys being added
            for (i, pubkey) in decoded.accounts.keys_to_add_accounts.iter().enumerate() {
                println!("  Account {}: {}", i, pubkey);
            }
        }
        PlayerProfileInstruction::CreateProfile(create_profile) => {
            println!("Creating profile with {} keys", create_profile.key_permissions.len());
            println!("Key threshold: {}", create_profile.key_threshold);

            // Access the initial key account pubkeys
            for pubkey in &decoded.accounts.init_keys_accounts {
                println!("  Init key: {}", pubkey);
            }
        }
        _ => {
            // Handle other instructions
        }
    }
}
```

**Note:** The `AddKeys` and `CreateProfile` instructions include both:
- **Instruction data** (`keys_to_add`, `key_permissions`) - Contains permission scopes and metadata
- **Account lists** (`keys_to_add_accounts`, `init_keys_accounts`) - Contains the actual pubkeys of the keys being added

### Working with Permissions

The decoder includes ergonomic permission handling with bitflags:

```rust
use carbon_player_profile_decoder::{ProfileKey, ProfilePermissions};

// Check if a key has specific permissions
let key: ProfileKey = /* ... */;

if key.is_auth() {
    println!("This is an auth key");
}

if key.has_permission(ProfilePermissions::ADD_KEYS) {
    println!("This key can add other keys");
}

// Check if a key has expired
let current_time = /* current unix timestamp */;
if key.is_expired(current_time) {
    println!("This key has expired");
}

// Get permission flags
let flags = key.permissions_flags();
if flags.contains(ProfilePermissions::CREATE_ROLE | ProfilePermissions::REMOVE_ROLE) {
    println!("This key can manage roles");
}
```

### Account Types

This decoder supports all Player Profile account types:
- `Profile` - Player profile with keys and metadata
- `PlayerName` - Player name registration
- `Role` - Role definition with permissions
- `ProfileRoleMembership` - Membership relationship between profiles and roles

### Account Fields (RemainingData)

All account types include dynamically-sized fields deserialized from RemainingData:

#### Profile
- `profile_keys: Vec<ProfileKey>` - List of all keys associated with this profile
  - Each key includes: public key, scope, expiration time, and permissions
  - Maximum of 65,535 keys (u16 length prefix)

#### PlayerName
- `name: Vec<u8>` - UTF-8 encoded player name bytes
  - Variable length, stored as raw bytes without length prefix
  - Example: `String::from_utf8_lossy(&player_name.name)`

#### Role
- `members: Vec<RoleMembership>` - List of all members in this role
  - Each membership includes: member key and status (Active/Inactive)
  - Maximum of 256 members (u32 length prefix, enforced by MAX_MEMBERSHIPS)

#### ProfileRoleMembership
- `memberships: Vec<RoleMembership>` - List of all roles this profile belongs to
  - Each membership includes: role key and status (Active/Inactive)
  - Maximum of 256 memberships (u32 length prefix, enforced by MAX_MEMBERSHIPS)

### Permission Flags

The `ProfilePermissions` bitflags type includes:
- `AUTH` - Auth key with full profile control
- `ADD_KEYS` - Can add non-auth keys
- `REMOVE_KEYS` - Can remove non-auth keys
- `CHANGE_NAME` - Can change player name
- `CREATE_ROLE` - Can create new roles
- `REMOVE_ROLE` - Can remove roles
- `SET_AUTHORIZER` - Can set role authorizer
- `JOIN_ROLE` - Can add profile to a role
- `LEAVE_ROLE` - Can remove profile from a role
- `TOGGLE_ACCEPTING_NEW_MEMBERS` - Can toggle accepting new members
- `ADD_MEMBER` - Can add members to roles
- `REMOVE_MEMBER` - Can remove members from roles
- `DRAIN_SOL_VAULT` - Can withdraw from SOL vault (scope-agnostic)

## Documentation

Full documentation is available at [docs.rs](https://docs.rs/carbon-player-profile-decoder).

## Repository

See the [main repository](https://github.com/staratlasmeta/star-atlas-decoders) for build instructions and contribution guidelines.

## License

Licensed under the [Apache-2.0](https://github.com/staratlasmeta/star-atlas-decoders/blob/main/LICENSE) license.