ai-registry 0.1.0

AI Registry on Solana - On-chain agent identity and knowledge management
Documentation
use ai_registry_api::{
    consts::AGENT, error::AgentError, event::ControllerTransferredEvent,
    instruction::TransferController, state::Agent,
};
use steel::*;

/// Process a transfer controller instruction
pub fn process_transfer_controller(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult {
    let args = TransferController::try_from_bytes(data)?;

    // Load accounts (signer first, following Solana best practices)
    let [controller_info, agent_info] = accounts else {
        return Err(ProgramError::NotEnoughAccountKeys);
    };

    // Validate accounts
    agent_info.is_writable()?.has_seeds(
        &[
            AGENT,
            agent_info
                .as_account::<Agent>(&ai_registry_api::ID)?
                .creator
                .as_ref(),
        ],
        &ai_registry_api::ID,
    )?;
    controller_info.is_signer()?;

    // Load agent and verify controller
    let agent = agent_info.as_account::<Agent>(&ai_registry_api::ID)?;
    if controller_info.key != &agent.controller {
        return Err(AgentError::UnauthorizedController.into());
    }

    // Validate new controller
    if args.new_controller == Pubkey::default() {
        return Err(AgentError::InvalidController.into());
    }

    if args.new_controller == agent.controller {
        // No change needed, but we'll allow it (idempotent)
    }

    // Transfer controller
    let clock = Clock::get()?;
    let agent = agent_info.as_account_mut::<Agent>(&ai_registry_api::ID)?;
    let old_controller = agent.controller;
    agent.controller = args.new_controller;
    agent.updated_at = clock.unix_timestamp;

    // Emit event
    ControllerTransferredEvent {
        creator: agent.creator,
        agent: *agent_info.key,
        old_controller,
        new_controller: args.new_controller,
        timestamp: clock.unix_timestamp,
    }
    .log();

    Ok(())
}