Files
2026-08-31 13:07:56 +02:00

6.4 KiB

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

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

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 :

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 :

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 :

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é :

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 :

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 :

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<TransportDto>.

La conversion appartient au composant qui connaît les deux côtés :

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 :

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 :

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.