# Usage de ksp-interface-lib Cette page décrit l'utilisation durable de la façade publique. Les modules internes ne font pas partie du contrat consommable : utiliser uniquement les exports du crate-root. ## Construire des account metas ```rust let readonly_account = ksp_interface_lib::Pubkey::new_from_array([1_u8; 32]); let writable_account = ksp_interface_lib::Pubkey::new_from_array([2_u8; 32]); let readonly = ksp_interface_lib::ProgramAccountMeta::readonly(readonly_account, true); let writable = ksp_interface_lib::ProgramAccountMeta::writable(writable_account, false); assert_eq!(readonly.pubkey(), &readonly_account); assert!(readonly.is_signer()); assert!(!readonly.is_writable()); assert_eq!(writable.pubkey(), &writable_account); assert!(!writable.is_signer()); assert!(writable.is_writable()); ``` `readonly`/`writable` décrivent uniquement les flags wire de l'account meta. Interface ne valide pas l'identité du compte contre un registry Program. ## Construire une instruction passive ```rust let program_id = ksp_interface_lib::Pubkey::new_from_array([3_u8; 32]); let account_id = ksp_interface_lib::Pubkey::new_from_array([4_u8; 32]); let account = ksp_interface_lib::ProgramAccountMeta::writable(account_id, true); let result = ksp_interface_lib::ProgramInstruction::try_new( program_id, std::vec![account, account], std::vec![7_u8, 8, 9], ); match result { std::result::Result::Ok(instruction) => { assert_eq!(instruction.program_id(), &program_id); assert_eq!(instruction.accounts(), &[account, account]); assert_eq!(instruction.data(), &[7_u8, 8, 9]); } std::result::Result::Err(error) => { eprintln!("{error}"); } } ``` L'ordre et les doublons des accounts sont conservés. Les octets `data` restent opaques : `ProgramInstruction` ne les sérialise, désérialise ni interprète. Les `Vec` fournis à `try_new` sont consommés par la structure après validation des bornes ; aucun clone ou reformatage interne n'est requis par le contrat. ## Respecter les bornes Program Les limites publiques sont : ```rust assert_eq!(ksp_interface_lib::MAX_PROGRAM_INSTRUCTION_ACCOUNTS, 255); assert_eq!(ksp_interface_lib::MAX_PROGRAM_INSTRUCTION_DATA_LEN, 10_240); ``` `255` account metas et `10_240` bytes de data sont admis. `256` account metas ou `10_241` bytes sont refusés par `ProgramInstruction::try_new`. Ces limites sont locales à Interface et ne promettent pas qu'une instruction admise respecte à elle seule toutes les contraintes d'une transaction Solana complète. ## Observer une erreur de limite Le code public est : ```rust assert_eq!( ksp_interface_lib::ERROR_CODE_PROGRAM_INSTRUCTION_LIMIT_EXCEEDED.domain(), "interface", ); assert_eq!( ksp_interface_lib::ERROR_CODE_PROGRAM_INSTRUCTION_LIMIT_EXCEEDED.code(), "program_instruction_limit_exceeded", ); ``` Une erreur de dépassement expose uniquement un contexte structurel sûr : ```text field actual_len maximum_len ``` Le contenu du payload et les account metas arbitraires ne sont pas projetés dans le diagnostic. ## Construire un événement de lifecycle de slot Un producer/composer qui a déjà établi la correspondance sémantique avec son DTO Transport peut construire le fait passif partagé : ```rust let event = ksp_interface_lib::SlotLifecycleEvent::new( 42, ksp_interface_lib::SlotLifecycleStage::Processed, ); assert_eq!(event.slot(), 42); assert_eq!(event.stage(), ksp_interface_lib::SlotLifecycleStage::Processed); ``` Les stages actuellement disponibles sont : ```text Processed FirstShredReceived Completed CreatedBank Dead OptimisticallyConfirmed Rooted ``` Un consumer externe doit traiter `SlotLifecycleStage` comme une enum évolutive `#[non_exhaustive]` et prévoir un fallback dans ses `match`. Le type ne contient volontairement ni timestamp, ni parent, ni diagnostic, ni source/provider, ni identifiant de subscription. ## Construire un événement d'exécution de transaction Une signature doit d'abord être disponible sous sa forme canonique de 64 bytes : ```rust let signature = ksp_interface_lib::TransactionSignature::new([7_u8; 64]); let event = ksp_interface_lib::TransactionExecutionEvent::new( 123, signature, ksp_interface_lib::TransactionExecutionOutcome::Succeeded, ); assert_eq!(event.slot(), 123); assert_eq!(event.signature().as_bytes(), &[7_u8; 64]); assert_eq!( event.outcome(), ksp_interface_lib::TransactionExecutionOutcome::Succeeded, ); ``` `TransactionExecutionOutcome` distingue uniquement `Succeeded` et `Failed` et reste `#[non_exhaustive]`. Le `Debug` de `TransactionSignature` et de `TransactionExecutionEvent` n'affiche pas les bytes de signature. ## Convertir depuis Transport Ne pas ajouter `ksp-onchain-transport-lib` comme dépendance de `ksp-interface-lib` pour fournir des `From`. La conversion appartient au composant qui connaît les deux côtés : ```text Transport DTO riche | | conversion explicite dans composition/consumer v Interface event passif minimal ``` Ne construire un événement Interface que si la source fournit suffisamment d'information pour le fait commun exact. Si l'état est ambigu, conserver le DTO dans son owner Transport ou déclencher une hydratation adaptée ; ne pas inventer de valeur par défaut. ## Ne pas utiliser les événements comme stockage durable `SlotLifecycleEvent` et `TransactionExecutionEvent` sont des faits passifs, pas des modèles RAW replayables. Ils ne remplacent pas : ```text RawTransaction RawTransactionObservation RawAccountState RawAccountObservation Store backlog / cursor / retention ``` Les consumers qui ont besoin de reprise après crash ou de replay doivent s'appuyer sur `ksp-store-lib`/`ksp-store-api` selon leur responsabilité, pas sur un event Interface en mémoire. ## Dépendances à ne pas ajouter côté consumer Un consumer de la façade Interface n'a pas besoin d'ajouter un SDK Program Solana uniquement pour reconstruire `ProgramInstruction`, ni un runtime Transport pour manipuler les événements passifs déjà normalisés. La crate ne fournit volontairement pas : ```text serde générique Borsh / Wincode générique solana-instruction interop automatique transport réseau converters provider automatiques Program decoder/preparer signing/execution persistence/event bus ``` Ces surfaces doivent être introduites dans leur owner respectif lorsqu'un cas réel le justifie.