Files
khadhroony-solana-project/crates/ksp-program-api/USAGE.md
2026-08-28 16:16:15 +02:00

5.4 KiB

Usage de ksp-program-api

Cette page décrit la surface publique candidate de 0.2.14. Utiliser uniquement les exports du crate-root ; aucun module interne ne fait partie du contrat consommable.

Construire un input Program avec la façade

let program_id = ksp_program_api::Pubkey::new_from_array([1_u8; 32]);
let account_id = ksp_program_api::Pubkey::new_from_array([2_u8; 32]);
let account = ksp_program_api::ProgramAccountMeta::readonly(account_id, true);

let instruction = ksp_program_api::ProgramInstruction::try_new(
    program_id,
    std::vec![account],
    std::vec![0x01_u8, 0x02, 0x03],
);

assert!(instruction.is_ok());

Pubkey, ProgramAccountMeta et ProgramInstruction conservent leur ownership Core/Interface. Les bornes 255 account metas et 10_240 bytes de data sont appliquées par Interface avant l'entrée dans le decoder.

Implémenter un decoder externe

Le type décodé reste entièrement possédé par la crate d'implémentation :

struct ExternalDecodedInstruction {
    opcode: u8,
}

struct ExternalDecoder {
    program_ids: [ksp_program_api::Pubkey; 1],
}

impl ksp_program_api::ProgramInstructionDecoder for ExternalDecoder {
    type Decoded = ExternalDecodedInstruction;

    fn program_ids(&self) -> &[ksp_program_api::Pubkey] {
        return &self.program_ids;
    }

    fn recognize(
        &self,
        instruction: &ksp_program_api::ProgramInstruction,
    ) -> ksp_program_api::ProgramInstructionRecognition {
        if instruction.program_id() != &self.program_ids[0] {
            return ksp_program_api::ProgramInstructionRecognition::NoMatch;
        }

        if instruction.data().first() == std::option::Option::Some(&0x2A_u8) {
            return ksp_program_api::ProgramInstructionRecognition::ExactMatch;
        }

        return ksp_program_api::ProgramInstructionRecognition::ProgramMatch;
    }

    fn decode(
        &self,
        instruction: &ksp_program_api::ProgramInstruction,
    ) -> ksp_program_api::Result<ksp_program_api::ProgramInstructionDecodeOutcome<Self::Decoded>> {
        let opcode = match instruction.data().first() {
            std::option::Option::Some(value) if *value == 0x2A_u8 => *value,
            _ => {
                return std::result::Result::Ok(
                    ksp_program_api::ProgramInstructionDecodeOutcome::Unsupported,
                );
            }
        };

        return std::result::Result::Ok(
            ksp_program_api::ProgramInstructionDecodeOutcome::Decoded(
                ExternalDecodedInstruction { opcode },
            ),
        );
    }
}

Aucun ksp-program-lib, enum centrale, Any, JSON ou codec n'est nécessaire. Le Program ID peut être un Pubkey opaque non enregistré par Core.

Sélection explicite

La sélection reste distincte du décodage :

let recognition = ksp_program_api::ProgramInstructionDecoder::recognize(
    &decoder,
    &instruction,
);

match recognition {
    ksp_program_api::ProgramInstructionRecognition::NoMatch => {}
    ksp_program_api::ProgramInstructionRecognition::ProgramMatch => {}
    ksp_program_api::ProgramInstructionRecognition::ExactMatch => {}
    _ => {}
}

L'enum est #[non_exhaustive]. ExactMatch exprime l'affirmation du decoder. decode n'est pas un substitut à recognize : il traite une instruction déjà sélectionnée pour cette implémentation.

Outcome et erreur

let outcome = ksp_program_api::ProgramInstructionDecoder::decode(
    &decoder,
    &instruction,
)?;

match outcome {
    ksp_program_api::ProgramInstructionDecodeOutcome::Decoded(value) => {
        let _opcode = value.opcode;
    }
    ksp_program_api::ProgramInstructionDecodeOutcome::Unsupported => {}
    _ => {}
}

Une erreur réelle est un Err(ksp_program_api::Error). Unsupported n'est pas une deuxième forme d'erreur : il indique qu'une instruction reconnue n'est volontairement pas décodée par cette capability.

Program API ne recopie automatiquement ni le payload de l'instruction ni les account metas dans l'erreur. Une implémentation externe reste responsable des messages/contextes qu'elle construit explicitement.

Output sans bounds implicites

L'associated type Decoded n'impose pas Debug, Clone, Send ou Sync. Les supertraits Send + Sync s'appliquent au decoder lui-même, pas à la valeur décodée :

struct LocalDecoded(std::rc::Rc<std::cell::Cell<u8>>);

Un decoder peut utiliser ce type comme Decoded tant que son propre état satisfait Send + Sync.

Debug sûr

ProgramInstructionDecodeOutcome<Decoded> possède un Debug volontairement opaque :

struct SecretDecoded;

let outcome = ksp_program_api::ProgramInstructionDecodeOutcome::Decoded(SecretDecoded);
assert_eq!(std::format!("{outcome:?}"), "Decoded");

SecretDecoded n'a pas besoin d'implémenter Debug et sa valeur n'est jamais rendue par l'outcome.

Ce qui n'est pas simulé côté consumer

Il n'existe dans 0.2.14 aucun :

registry de decoders
composition dyn hétérogène
identity/version/coverage descriptor
priority/conflict policy
payload canonique D3
ProgramAccountDecoder / Event / ReturnData
ProgramExecutionPreparer
execution policy
serde / JSON / codec
logging / runtime réseau

Ces surfaces ne doivent pas être recréées localement comme si elles faisaient déjà partie du contrat commun. Elles attendent les vertical slices qui justifieront leurs invariants réels.